<div dir="ltr"><div dir="ltr"><br></div><br><div class="gmail_quote"><div dir="ltr" class="gmail_attr">On Mon, Feb 10, 2020 at 11:59 PM Mikael Holmén via llvm-commits <<a href="mailto:llvm-commits@lists.llvm.org">llvm-commits@lists.llvm.org</a>> wrote:<br></div><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left:1px solid rgb(204,204,204);padding-left:1ex">Hi Peter,<br>
<br>
I get two warnings with this commit when compiling with clang 8.0:<br>
<br>
/repo/uabelho/master/compiler-<br>
rt/lib/scudo/standalone/size_class_map.h:122:20: error: implicit<br>
conversion loses integer precision: 'unsigned long' to 'scudo::u8' (aka<br>
'unsigned char') [-Werror,-Wimplicit-int-conversion]<br>
          return i + 1;<br>
          ~~~~~~ ~~^~~<br>
/repo/uabelho/master/compiler-<br>
rt/lib/scudo/standalone/size_class_map.h:124:14: error: implicit<br>
conversion changes signedness: 'int' to 'scudo::u8' (aka 'unsigned<br>
char') [-Werror,-Wsign-conversion]<br>
      return -1;<br>
      ~~~~~~ ^~<br>
2 errors generated.<br></blockquote><div><br></div><div>I get these as well, and when I work around them by adding casts I get an additional failure:</div><div><br></div>compiler-rt/lib/scudo/standalone/size_class_map.h:27:21: error: use of undeclared identifier 'MaxSize'<br>    DCHECK_LE(Size, MaxSize);<br><div> </div><div>Teresa</div><div><br></div><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left:1px solid rgb(204,204,204);padding-left:1ex">
<br>
Regards,<br>
Mikael<br>
<br>
On Mon, 2020-02-10 at 15:00 -0800, Peter Collingbourne via llvm-commits <br>
wrote:<br>
> Author: Peter Collingbourne<br>
> Date: 2020-02-10T14:59:49-08:00<br>
> New Revision: 041547eb4eb0fcb2155af7537aaed7f601ea6343<br>
> <br>
> URL: <br>
> <a href="https://protect2.fireeye.com/v1/url?k=11c8caed-4d43c1d7-11c88a76-86925ec6fd56-7b0dd73c95acf55a&q=1&e=3f260b55-66e1-4cd6-b863-a62c9b2a7eef&u=https%3A%2F%2Fgithub.com%2Fllvm%2Fllvm-project%2Fcommit%2F041547eb4eb0fcb2155af7537aaed7f601ea6343" rel="noreferrer" target="_blank">https://protect2.fireeye.com/v1/url?k=11c8caed-4d43c1d7-11c88a76-86925ec6fd56-7b0dd73c95acf55a&q=1&e=3f260b55-66e1-4cd6-b863-a62c9b2a7eef&u=https%3A%2F%2Fgithub.com%2Fllvm%2Fllvm-project%2Fcommit%2F041547eb4eb0fcb2155af7537aaed7f601ea6343</a><br>
> DIFF: <br>
> <a href="https://protect2.fireeye.com/v1/url?k=f659a75c-aad2ac66-f659e7c7-86925ec6fd56-3b7ca05c4f80392f&q=1&e=3f260b55-66e1-4cd6-b863-a62c9b2a7eef&u=https%3A%2F%2Fgithub.com%2Fllvm%2Fllvm-project%2Fcommit%2F041547eb4eb0fcb2155af7537aaed7f601ea6343.diff" rel="noreferrer" target="_blank">https://protect2.fireeye.com/v1/url?k=f659a75c-aad2ac66-f659e7c7-86925ec6fd56-3b7ca05c4f80392f&q=1&e=3f260b55-66e1-4cd6-b863-a62c9b2a7eef&u=https%3A%2F%2Fgithub.com%2Fllvm%2Fllvm-project%2Fcommit%2F041547eb4eb0fcb2155af7537aaed7f601ea6343.diff</a><br>
> <br>
> LOG: scudo: Table driven size classes for Android allocator.<br>
> <br>
> Add an optional table lookup after the existing logarithm computation<br>
> for MidSize < Size <= MaxSize during size -> class lookups. The<br>
> lookup is<br>
> O(1) due to indexing a precomputed (via constexpr) table based on a<br>
> size<br>
> table. Switch to this approach for the Android size class maps.<br>
> <br>
> Other approaches considered:<br>
> - Binary search was found to have an unacceptable (~30%) performance<br>
> cost.<br>
> - An approach using NEON instructions (see older version of D73824)<br>
> was found<br>
>   to be slightly slower than this approach on newer SoCs but<br>
> significantly<br>
>   slower on older ones.<br>
> <br>
> By selecting the values in the size tables to minimize wastage (for<br>
> example,<br>
> by passing the malloc_info output of a target program to the included<br>
> compute_size_class_config program), we can increase the density of<br>
> allocations<br>
> at a small (~0.5% on bionic malloc_sql_trace as measured using an<br>
> identity<br>
> table) performance cost.<br>
> <br>
> Reduces RSS on specific Android processes as follows (KB):<br>
> <br>
>                              Before  After<br>
> zygote (median of 50 runs)    26836  26792 (-0.2%)<br>
> zygote64 (median of 50 runs)  30384  30076 (-1.0%)<br>
> dex2oat (median of 3 runs)   375792 372952 (-0.8%)<br>
> <br>
> I also measured the amount of whole-system idle dirty heap on Android<br>
> by<br>
> rebooting the system and then running the following script repeatedly<br>
> until<br>
> the results were stable:<br>
> <br>
> for i in $(seq 1 50); do grep -A5 scudo: /proc/*/smaps | grep Pss: |<br>
> cut -d: -f2 | awk '{s+=$1} END {print s}' ; sleep 1; done<br>
> <br>
> I did this 3 times both before and after this change and the results<br>
> were:<br>
> <br>
> Before: 365650, 356795, 372663<br>
> After:  344521, 356328, 342589<br>
> <br>
> These results are noisy so it is hard to make a definite conclusion,<br>
> but<br>
> there does appear to be a significant effect.<br>
> <br>
> On other platforms, increase the sizes of all size classes by a fixed<br>
> offset<br>
> equal to the size of the allocation header. This has also been found<br>
> to improve<br>
> density, since it is likely for allocation sizes to be a power of 2,<br>
> which<br>
> would otherwise waste space by pushing the allocation into the next<br>
> size class.<br>
> <br>
> Differential Revision: <br>
> <a href="https://protect2.fireeye.com/v1/url?k=bec7d9b6-e24cd28c-bec7992d-86925ec6fd56-15488a4caaa49981&q=1&e=3f260b55-66e1-4cd6-b863-a62c9b2a7eef&u=https%3A%2F%2Freviews.llvm.org%2FD73824" rel="noreferrer" target="_blank">https://protect2.fireeye.com/v1/url?k=bec7d9b6-e24cd28c-bec7992d-86925ec6fd56-15488a4caaa49981&q=1&e=3f260b55-66e1-4cd6-b863-a62c9b2a7eef&u=https%3A%2F%2Freviews.llvm.org%2FD73824</a><br>
> <br>
> Added: <br>
>     compiler-<br>
> rt/lib/scudo/standalone/tools/compute_size_class_config.cpp<br>
> <br>
> Modified: <br>
>     compiler-rt/lib/scudo/standalone/size_class_map.h<br>
>     compiler-rt/lib/scudo/standalone/tests/combined_test.cpp<br>
>     compiler-rt/lib/scudo/standalone/tests/size_class_map_test.cpp<br>
> <br>
> Removed: <br>
>     <br>
> <br>
> <br>
> #####################################################################<br>
> ###########<br>
> diff  --git a/compiler-rt/lib/scudo/standalone/size_class_map.h<br>
> b/compiler-rt/lib/scudo/standalone/size_class_map.h<br>
> index ff587c97955d..46f53ae51fba 100644<br>
> --- a/compiler-rt/lib/scudo/standalone/size_class_map.h<br>
> +++ b/compiler-rt/lib/scudo/standalone/size_class_map.h<br>
> @@ -9,11 +9,33 @@<br>
>  #ifndef SCUDO_SIZE_CLASS_MAP_H_<br>
>  #define SCUDO_SIZE_CLASS_MAP_H_<br>
>  <br>
> +#include "chunk.h"<br>
>  #include "common.h"<br>
>  #include "string_utils.h"<br>
>  <br>
>  namespace scudo {<br>
>  <br>
> +inline uptr scaledLog2(uptr Size, uptr ZeroLog, uptr LogBits) {<br>
> +  const uptr L = getMostSignificantSetBitIndex(Size);<br>
> +  const uptr LBits = (Size >> (L - LogBits)) - (1 << LogBits);<br>
> +  const uptr HBits = (L - ZeroLog) << LogBits;<br>
> +  return LBits + HBits;<br>
> +}<br>
> +<br>
> +template <typename Config> struct SizeClassMapBase {<br>
> +  static u32 getMaxCachedHint(uptr Size) {<br>
> +    DCHECK_LE(Size, MaxSize);<br>
> +    DCHECK_NE(Size, 0);<br>
> +    u32 N;<br>
> +    // Force a 32-bit division if the template parameters allow for<br>
> it.<br>
> +    if (Config::MaxBytesCachedLog > 31 || Config::MaxSizeLog > 31)<br>
> +      N = static_cast<u32>((1UL << Config::MaxBytesCachedLog) /<br>
> Size);<br>
> +    else<br>
> +      N = (1U << Config::MaxBytesCachedLog) /<br>
> static_cast<u32>(Size);<br>
> +    return Max(1U, Min(Config::MaxNumCachedHint, N));<br>
> +  }<br>
> +};<br>
> +<br>
>  // SizeClassMap maps allocation sizes into size classes and back, in<br>
> an<br>
>  // efficient table-free manner.<br>
>  //<br>
> @@ -33,22 +55,24 @@ namespace scudo {<br>
>  // of chunks that can be cached per-thread:<br>
>  // - MaxNumCachedHint is a hint for the max number of chunks cached<br>
> per class.<br>
>  // - 2^MaxBytesCachedLog is the max number of bytes cached per<br>
> class.<br>
> +template <typename Config><br>
> +class FixedSizeClassMap : public SizeClassMapBase<Config> {<br>
> +  typedef SizeClassMapBase<Config> Base;<br>
>  <br>
> -template <u8 NumBits, u8 MinSizeLog, u8 MidSizeLog, u8 MaxSizeLog,<br>
> -          u32 MaxNumCachedHintT, u8 MaxBytesCachedLog><br>
> -class SizeClassMap {<br>
> -  static const uptr MinSize = 1UL << MinSizeLog;<br>
> -  static const uptr MidSize = 1UL << MidSizeLog;<br>
> +  static const uptr MinSize = 1UL << Config::MinSizeLog;<br>
> +  static const uptr MidSize = 1UL << Config::MidSizeLog;<br>
>    static const uptr MidClass = MidSize / MinSize;<br>
> -  static const u8 S = NumBits - 1;<br>
> +  static const u8 S = Config::NumBits - 1;<br>
>    static const uptr M = (1UL << S) - 1;<br>
>  <br>
> +  static const uptr SizeDelta = Chunk::getHeaderSize();<br>
> +<br>
>  public:<br>
> -  static const u32 MaxNumCachedHint = MaxNumCachedHintT;<br>
> +  static const u32 MaxNumCachedHint = Config::MaxNumCachedHint;<br>
>  <br>
> -  static const uptr MaxSize = 1UL << MaxSizeLog;<br>
> +  static const uptr MaxSize = (1UL << Config::MaxSizeLog) +<br>
> SizeDelta;<br>
>    static const uptr NumClasses =<br>
> -      MidClass + ((MaxSizeLog - MidSizeLog) << S) + 1;<br>
> +      MidClass + ((Config::MaxSizeLog - Config::MidSizeLog) << S) +<br>
> 1;<br>
>    static_assert(NumClasses <= 256, "");<br>
>    static const uptr LargestClassId = NumClasses - 1;<br>
>    static const uptr BatchClassId = 0;<br>
> @@ -56,97 +80,206 @@ class SizeClassMap {<br>
>    static uptr getSizeByClassId(uptr ClassId) {<br>
>      DCHECK_NE(ClassId, BatchClassId);<br>
>      if (ClassId <= MidClass)<br>
> -      return ClassId << MinSizeLog;<br>
> +      return (ClassId << Config::MinSizeLog) + SizeDelta;<br>
>      ClassId -= MidClass;<br>
>      const uptr T = MidSize << (ClassId >> S);<br>
> -    return T + (T >> S) * (ClassId & M);<br>
> +    return T + (T >> S) * (ClassId & M) + SizeDelta;<br>
>    }<br>
>  <br>
>    static uptr getClassIdBySize(uptr Size) {<br>
> +    if (Size <= SizeDelta + (1 << Config::MinSizeLog))<br>
> +      return 1;<br>
> +    Size -= SizeDelta;<br>
>      DCHECK_LE(Size, MaxSize);<br>
>      if (Size <= MidSize)<br>
> -      return (Size + MinSize - 1) >> MinSizeLog;<br>
> -    Size -= 1;<br>
> -    const uptr L = getMostSignificantSetBitIndex(Size);<br>
> -    const uptr LBits = (Size >> (L - S)) - (1 << S);<br>
> -    const uptr HBits = (L - MidSizeLog) << S;<br>
> -    return MidClass + 1 + HBits + LBits;<br>
> +      return (Size + MinSize - 1) >> Config::MinSizeLog;<br>
> +    return MidClass + 1 + scaledLog2(Size - 1, Config::MidSizeLog,<br>
> S);<br>
>    }<br>
> +};<br>
>  <br>
> -  static u32 getMaxCachedHint(uptr Size) {<br>
> -    DCHECK_LE(Size, MaxSize);<br>
> -    DCHECK_NE(Size, 0);<br>
> -    u32 N;<br>
> -    // Force a 32-bit division if the template parameters allow for<br>
> it.<br>
> -    if (MaxBytesCachedLog > 31 || MaxSizeLog > 31)<br>
> -      N = static_cast<u32>((1UL << MaxBytesCachedLog) / Size);<br>
> -    else<br>
> -      N = (1U << MaxBytesCachedLog) / static_cast<u32>(Size);<br>
> -    return Max(1U, Min(MaxNumCachedHint, N));<br>
> -  }<br>
> +template <typename Config><br>
> +class TableSizeClassMap : public SizeClassMapBase<Config> {<br>
> +  static const u8 S = Config::NumBits - 1;<br>
> +  static const uptr M = (1UL << S) - 1;<br>
> +  static const uptr ClassesSize =<br>
> +      sizeof(Config::Classes) / sizeof(Config::Classes[0]);<br>
>  <br>
> -  static void print() {<br>
> -    ScopedString Buffer(1024);<br>
> -    uptr PrevS = 0;<br>
> -    uptr TotalCached = 0;<br>
> -    for (uptr I = 0; I < NumClasses; I++) {<br>
> -      if (I == BatchClassId)<br>
> -        continue;<br>
> -      const uptr S = getSizeByClassId(I);<br>
> -      if (S >= MidSize / 2 && (S & (S - 1)) == 0)<br>
> -        Buffer.append("\n");<br>
> -      const uptr D = S - PrevS;<br>
> -      const uptr P = PrevS ? (D * 100 / PrevS) : 0;<br>
> -      const uptr L = S ? getMostSignificantSetBitIndex(S) : 0;<br>
> -      const uptr Cached = getMaxCachedHint(S) * S;<br>
> -      Buffer.append(<br>
> -          "C%02zu => S: %zu <br>
> diff : +%zu %02zu%% L %zu Cached: %zu %zu; id %zu\n",<br>
> -          I, getSizeByClassId(I), D, P, L, getMaxCachedHint(S),<br>
> Cached,<br>
> -          getClassIdBySize(S));<br>
> -      TotalCached += Cached;<br>
> -      PrevS = S;<br>
> +  struct SizeTable {<br>
> +    constexpr SizeTable() {<br>
> +      uptr Pos = 1 << Config::MidSizeLog;<br>
> +      uptr Inc = 1 << (Config::MidSizeLog - S);<br>
> +      for (uptr i = 0; i != getTableSize(); ++i) {<br>
> +        Pos += Inc;<br>
> +        if ((Pos & (Pos - 1)) == 0)<br>
> +          Inc *= 2;<br>
> +        Tab[i] = computeClassId(Pos + Config::SizeDelta);<br>
> +      }<br>
>      }<br>
> -    Buffer.append("Total Cached: %zu\n", TotalCached);<br>
> -    Buffer.output();<br>
> -  }<br>
>  <br>
> -  static void validate() {<br>
> -    for (uptr C = 0; C < NumClasses; C++) {<br>
> -      if (C == BatchClassId)<br>
> -        continue;<br>
> -      const uptr S = getSizeByClassId(C);<br>
> -      CHECK_NE(S, 0U);<br>
> -      CHECK_EQ(getClassIdBySize(S), C);<br>
> -      if (C < LargestClassId)<br>
> -        CHECK_EQ(getClassIdBySize(S + 1), C + 1);<br>
> -      CHECK_EQ(getClassIdBySize(S - 1), C);<br>
> -      if (C - 1 != BatchClassId)<br>
> -        CHECK_GT(getSizeByClassId(C), getSizeByClassId(C - 1));<br>
> +    constexpr static u8 computeClassId(uptr Size) {<br>
> +      for (uptr i = 0; i != ClassesSize; ++i) {<br>
> +        if (Size <= Config::Classes[i])<br>
> +          return i + 1;<br>
> +      }<br>
> +      return -1;<br>
>      }<br>
> -    // Do not perform the loop if the maximum size is too large.<br>
> -    if (MaxSizeLog > 19)<br>
> -      return;<br>
> -    for (uptr S = 1; S <= MaxSize; S++) {<br>
> -      const uptr C = getClassIdBySize(S);<br>
> -      CHECK_LT(C, NumClasses);<br>
> -      CHECK_GE(getSizeByClassId(C), S);<br>
> -      if (C - 1 != BatchClassId)<br>
> -        CHECK_LT(getSizeByClassId(C - 1), S);<br>
> +<br>
> +    constexpr static uptr getTableSize() {<br>
> +      return (Config::MaxSizeLog - Config::MidSizeLog) << S;<br>
>      }<br>
> +<br>
> +    u8 Tab[getTableSize()] = {};<br>
> +  };<br>
> +<br>
> +  static constexpr SizeTable Table = {};<br>
> +<br>
> +public:<br>
> +  static const u32 MaxNumCachedHint = Config::MaxNumCachedHint;<br>
> +<br>
> +  static const uptr NumClasses = ClassesSize + 1;<br>
> +  static_assert(NumClasses < 256, "");<br>
> +  static const uptr LargestClassId = NumClasses - 1;<br>
> +  static const uptr BatchClassId = 0;<br>
> +  static const uptr MaxSize = Config::Classes[LargestClassId - 1];<br>
> +<br>
> +  static uptr getSizeByClassId(uptr ClassId) {<br>
> +    return Config::Classes[ClassId - 1];<br>
>    }<br>
> +<br>
> +  static uptr getClassIdBySize(uptr Size) {<br>
> +    if (Size <= Config::Classes[0])<br>
> +      return 1;<br>
> +    Size -= Config::SizeDelta;<br>
> +    DCHECK_LE(Size, MaxSize);<br>
> +    if (Size <= (1 << Config::MidSizeLog))<br>
> +      return ((Size - 1) >> Config::MinSizeLog) + 1;<br>
> +    return Table.Tab[scaledLog2(Size - 1, Config::MidSizeLog, S)];<br>
> +  }<br>
> +<br>
> +  static void print() {}<br>
> +  static void validate() {}<br>
> +};<br>
> +<br>
> +struct AndroidSizeClassConfig {<br>
> +#if SCUDO_WORDSIZE == 64U<br>
> +  // Measured using a system_server profile.<br>
> +  static const uptr NumBits = 7;<br>
> +  static const uptr MinSizeLog = 4;<br>
> +  static const uptr MidSizeLog = 6;<br>
> +  static const uptr MaxSizeLog = 16;<br>
> +  static const u32 MaxNumCachedHint = 14;<br>
> +  static const uptr MaxBytesCachedLog = 14;<br>
> +<br>
> +  static constexpr u32 Classes[] = {<br>
> +      0x00020, 0x00030, 0x00040, 0x00050, 0x00060, 0x00070, 0x00090,<br>
> 0x000a0,<br>
> +      0x000b0, 0x000e0, 0x00110, 0x00130, 0x001a0, 0x00240, 0x00320,<br>
> 0x00430,<br>
> +      0x00640, 0x00830, 0x00a10, 0x00c30, 0x01010, 0x01150, 0x01ad0,<br>
> 0x02190,<br>
> +      0x03610, 0x04010, 0x04510, 0x04d10, 0x05a10, 0x07310, 0x09610,<br>
> 0x10010,<br>
> +  };<br>
> +  static const uptr SizeDelta = 16;<br>
> +#else<br>
> +  // Measured using a dex2oat profile.<br>
> +  static const uptr NumBits = 8;<br>
> +  static const uptr MinSizeLog = 4;<br>
> +  static const uptr MidSizeLog = 8;<br>
> +  static const uptr MaxSizeLog = 16;<br>
> +  static const u32 MaxNumCachedHint = 14;<br>
> +  static const uptr MaxBytesCachedLog = 14;<br>
> +<br>
> +  static constexpr u32 Classes[] = {<br>
> +      0x00020, 0x00030, 0x00040, 0x00050, 0x00060, 0x00070, 0x00080,<br>
> 0x00090,<br>
> +      0x000a0, 0x000b0, 0x000c0, 0x000d0, 0x000e0, 0x000f0, 0x00100,<br>
> 0x00110,<br>
> +      0x00120, 0x00140, 0x00150, 0x00170, 0x00190, 0x001c0, 0x001f0,<br>
> 0x00220,<br>
> +      0x00240, 0x00260, 0x002a0, 0x002e0, 0x00310, 0x00340, 0x00380,<br>
> 0x003b0,<br>
> +      0x003e0, 0x00430, 0x00490, 0x00500, 0x00570, 0x005f0, 0x00680,<br>
> 0x00720,<br>
> +      0x007d0, 0x00890, 0x00970, 0x00a50, 0x00b80, 0x00cb0, 0x00e30,<br>
> 0x00fb0,<br>
> +      0x011b0, 0x01310, 0x01470, 0x01790, 0x01b50, 0x01fd0, 0x02310,<br>
> 0x02690,<br>
> +      0x02b10, 0x02fd0, 0x03610, 0x03e10, 0x04890, 0x05710, 0x06a90,<br>
> 0x10010,<br>
> +  };<br>
> +  static const uptr SizeDelta = 16;<br>
> +#endif<br>
> +};<br>
> +<br>
> +typedef TableSizeClassMap<AndroidSizeClassConfig><br>
> AndroidSizeClassMap;<br>
> +<br>
> +struct DefaultSizeClassConfig {<br>
> +  static const uptr NumBits = 3;<br>
> +  static const uptr MinSizeLog = 5;<br>
> +  static const uptr MidSizeLog = 8;<br>
> +  static const uptr MaxSizeLog = 17;<br>
> +  static const u32 MaxNumCachedHint = 8;<br>
> +  static const uptr MaxBytesCachedLog = 10;<br>
>  };<br>
>  <br>
> -typedef SizeClassMap<3, 5, 8, 17, 8, 10> DefaultSizeClassMap;<br>
> +typedef FixedSizeClassMap<DefaultSizeClassConfig><br>
> DefaultSizeClassMap;<br>
>  <br>
> -// TODO(kostyak): further tune class maps for Android & Fuchsia.<br>
> +struct SvelteSizeClassConfig {<br>
>  #if SCUDO_WORDSIZE == 64U<br>
> -typedef SizeClassMap<4, 4, 8, 14, 4, 10> SvelteSizeClassMap;<br>
> -typedef SizeClassMap<2, 5, 9, 16, 14, 14> AndroidSizeClassMap;<br>
> +  static const uptr NumBits = 4;<br>
> +  static const uptr MinSizeLog = 4;<br>
> +  static const uptr MidSizeLog = 8;<br>
> +  static const uptr MaxSizeLog = 14;<br>
> +  static const u32 MaxNumCachedHint = 4;<br>
> +  static const uptr MaxBytesCachedLog = 10;<br>
>  #else<br>
> -typedef SizeClassMap<4, 3, 7, 14, 5, 10> SvelteSizeClassMap;<br>
> -typedef SizeClassMap<3, 4, 9, 16, 14, 14> AndroidSizeClassMap;<br>
> +  static const uptr NumBits = 4;<br>
> +  static const uptr MinSizeLog = 3;<br>
> +  static const uptr MidSizeLog = 7;<br>
> +  static const uptr MaxSizeLog = 14;<br>
> +  static const u32 MaxNumCachedHint = 5;<br>
> +  static const uptr MaxBytesCachedLog = 10;<br>
>  #endif<br>
> +};<br>
> +<br>
> +typedef FixedSizeClassMap<SvelteSizeClassConfig> SvelteSizeClassMap;<br>
> +<br>
> +template <typename SCMap> inline void printMap() {<br>
> +  ScopedString Buffer(1024);<br>
> +  uptr PrevS = 0;<br>
> +  uptr TotalCached = 0;<br>
> +  for (uptr I = 0; I < SCMap::NumClasses; I++) {<br>
> +    if (I == SCMap::BatchClassId)<br>
> +      continue;<br>
> +    const uptr S = SCMap::getSizeByClassId(I);<br>
> +    const uptr D = S - PrevS;<br>
> +    const uptr P = PrevS ? (D * 100 / PrevS) : 0;<br>
> +    const uptr L = S ? getMostSignificantSetBitIndex(S) : 0;<br>
> +    const uptr Cached = SCMap::getMaxCachedHint(S) * S;<br>
> +    Buffer.append(<br>
> +        "C%02zu => S: %zu <br>
> diff : +%zu %02zu%% L %zu Cached: %zu %zu; id %zu\n",<br>
> +        I, S, D, P, L, SCMap::getMaxCachedHint(S), Cached,<br>
> +        SCMap::getClassIdBySize(S));<br>
> +    TotalCached += Cached;<br>
> +    PrevS = S;<br>
> +  }<br>
> +  Buffer.append("Total Cached: %zu\n", TotalCached);<br>
> +  Buffer.output();<br>
> +}<br>
>  <br>
> +template <typename SCMap> static void validateMap() {<br>
> +  for (uptr C = 0; C < SCMap::NumClasses; C++) {<br>
> +    if (C == SCMap::BatchClassId)<br>
> +      continue;<br>
> +    const uptr S = SCMap::getSizeByClassId(C);<br>
> +    CHECK_NE(S, 0U);<br>
> +    CHECK_EQ(SCMap::getClassIdBySize(S), C);<br>
> +    if (C < SCMap::LargestClassId)<br>
> +      CHECK_EQ(SCMap::getClassIdBySize(S + 1), C + 1);<br>
> +    CHECK_EQ(SCMap::getClassIdBySize(S - 1), C);<br>
> +    if (C - 1 != SCMap::BatchClassId)<br>
> +      CHECK_GT(SCMap::getSizeByClassId(C), SCMap::getSizeByClassId(C<br>
> - 1));<br>
> +  }<br>
> +  // Do not perform the loop if the maximum size is too large.<br>
> +  if (SCMap::MaxSize > (1 << 19))<br>
> +    return;<br>
> +  for (uptr S = 1; S <= SCMap::MaxSize; S++) {<br>
> +    const uptr C = SCMap::getClassIdBySize(S);<br>
> +    CHECK_LT(C, SCMap::NumClasses);<br>
> +    CHECK_GE(SCMap::getSizeByClassId(C), S);<br>
> +    if (C - 1 != SCMap::BatchClassId)<br>
> +      CHECK_LT(SCMap::getSizeByClassId(C - 1), S);<br>
> +  }<br>
> +}<br>
>  } // namespace scudo<br>
>  <br>
>  #endif // SCUDO_SIZE_CLASS_MAP_H_<br>
> <br>
> diff  --git a/compiler-<br>
> rt/lib/scudo/standalone/tests/combined_test.cpp b/compiler-<br>
> rt/lib/scudo/standalone/tests/combined_test.cpp<br>
> index f6dc3d6ba672..488dca91a359 100644<br>
> --- a/compiler-rt/lib/scudo/standalone/tests/combined_test.cpp<br>
> +++ b/compiler-rt/lib/scudo/standalone/tests/combined_test.cpp<br>
> @@ -157,15 +157,16 @@ template <class Config> static void<br>
> testAllocator() {<br>
>  <br>
>    // Check that reallocating a chunk to a slightly smaller or larger<br>
> size<br>
>    // returns the same chunk. This requires that all the sizes we<br>
> iterate on use<br>
> -  // the same block size, but that should be the case for 2048 with<br>
> our default<br>
> -  // class size maps.<br>
> -  P = Allocator->allocate(DataSize, Origin);<br>
> -  memset(P, Marker, DataSize);<br>
> +  // the same block size, but that should be the case for MaxSize -<br>
> 64 with our<br>
> +  // default class size maps.<br>
> +  constexpr scudo::uptr ReallocSize = MaxSize - 64;<br>
> +  P = Allocator->allocate(ReallocSize, Origin);<br>
> +  memset(P, Marker, ReallocSize);<br>
>    for (scudo::sptr Delta = -32; Delta < 32; Delta += 8) {<br>
> -    const scudo::uptr NewSize = DataSize + Delta;<br>
> +    const scudo::uptr NewSize = ReallocSize + Delta;<br>
>      void *NewP = Allocator->reallocate(P, NewSize);<br>
>      EXPECT_EQ(NewP, P);<br>
> -    for (scudo::uptr I = 0; I < DataSize - 32; I++)<br>
> +    for (scudo::uptr I = 0; I < ReallocSize - 32; I++)<br>
>        EXPECT_EQ((reinterpret_cast<char *>(NewP))[I], Marker);<br>
>      checkMemoryTaggingMaybe(Allocator.get(), NewP, NewSize, 0);<br>
>    }<br>
> @@ -343,9 +344,19 @@ TEST(ScudoCombinedTest, ThreadedCombined) {<br>
>  #endif<br>
>  }<br>
>  <br>
> +<br>
> +struct DeathSizeClassConfig {<br>
> +  static const scudo::uptr NumBits = 1;<br>
> +  static const scudo::uptr MinSizeLog = 10;<br>
> +  static const scudo::uptr MidSizeLog = 10;<br>
> +  static const scudo::uptr MaxSizeLog = 10;<br>
> +  static const scudo::u32 MaxNumCachedHint = 1;<br>
> +  static const scudo::uptr MaxBytesCachedLog = 10;<br>
> +};<br>
> +<br>
>  struct DeathConfig {<br>
>    // Tiny allocator, its Primary only serves chunks of 1024 bytes.<br>
> -  using DeathSizeClassMap = scudo::SizeClassMap<1U, 10U, 10U, 10U,<br>
> 1U, 10U>;<br>
> +  using DeathSizeClassMap =<br>
> scudo::FixedSizeClassMap<DeathSizeClassConfig>;<br>
>    typedef scudo::SizeClassAllocator64<DeathSizeClassMap, 20U><br>
> Primary;<br>
>    typedef scudo::MapAllocator<scudo::MapAllocatorNoCache> Secondary;<br>
>    template <class A> using TSDRegistryT =<br>
> scudo::TSDRegistrySharedT<A, 1U>;<br>
> <br>
> diff  --git a/compiler-<br>
> rt/lib/scudo/standalone/tests/size_class_map_test.cpp b/compiler-<br>
> rt/lib/scudo/standalone/tests/size_class_map_test.cpp<br>
> index 55850400a765..c9e173f8e539 100644<br>
> --- a/compiler-rt/lib/scudo/standalone/tests/size_class_map_test.cpp<br>
> +++ b/compiler-rt/lib/scudo/standalone/tests/size_class_map_test.cpp<br>
> @@ -12,8 +12,8 @@<br>
>  <br>
>  template <class SizeClassMap> void testSizeClassMap() {<br>
>    typedef SizeClassMap SCMap;<br>
> -  SCMap::print();<br>
> -  SCMap::validate();<br>
> +  scudo::printMap<SCMap>();<br>
> +  scudo::validateMap<SCMap>();<br>
>  }<br>
>  <br>
>  TEST(ScudoSizeClassMapTest, DefaultSizeClassMap) {<br>
> @@ -28,12 +28,31 @@ TEST(ScudoSizeClassMapTest, AndroidSizeClassMap)<br>
> {<br>
>    testSizeClassMap<scudo::AndroidSizeClassMap>();<br>
>  }<br>
>  <br>
> +<br>
> +struct OneClassSizeClassConfig {<br>
> +  static const scudo::uptr NumBits = 1;<br>
> +  static const scudo::uptr MinSizeLog = 5;<br>
> +  static const scudo::uptr MidSizeLog = 5;<br>
> +  static const scudo::uptr MaxSizeLog = 5;<br>
> +  static const scudo::u32 MaxNumCachedHint = 0;<br>
> +  static const scudo::uptr MaxBytesCachedLog = 0;<br>
> +};<br>
> +<br>
>  TEST(ScudoSizeClassMapTest, OneClassSizeClassMap) {<br>
> -  testSizeClassMap<scudo::SizeClassMap<1, 5, 5, 5, 0, 0>>();<br>
> +  testSizeClassMap<scudo::FixedSizeClassMap<OneClassSizeClassConfig><br>
> >();<br>
>  }<br>
>  <br>
>  #if SCUDO_CAN_USE_PRIMARY64<br>
> +struct LargeMaxSizeClassConfig {<br>
> +  static const scudo::uptr NumBits = 3;<br>
> +  static const scudo::uptr MinSizeLog = 4;<br>
> +  static const scudo::uptr MidSizeLog = 8;<br>
> +  static const scudo::uptr MaxSizeLog = 63;<br>
> +  static const scudo::u32 MaxNumCachedHint = 128;<br>
> +  static const scudo::uptr MaxBytesCachedLog = 16;<br>
> +};<br>
> +<br>
>  TEST(ScudoSizeClassMapTest, LargeMaxSizeClassMap) {<br>
> -  testSizeClassMap<scudo::SizeClassMap<3, 4, 8, 63, 128, 16>>();<br>
> +  testSizeClassMap<scudo::FixedSizeClassMap<LargeMaxSizeClassConfig><br>
> >();<br>
>  }<br>
>  #endif<br>
> <br>
> diff  --git a/compiler-<br>
> rt/lib/scudo/standalone/tools/compute_size_class_config.cpp<br>
> b/compiler-<br>
> rt/lib/scudo/standalone/tools/compute_size_class_config.cpp<br>
> new file mode 100644<br>
> index 000000000000..82f37b6647ef<br>
> --- /dev/null<br>
> +++ b/compiler-<br>
> rt/lib/scudo/standalone/tools/compute_size_class_config.cpp<br>
> @@ -0,0 +1,161 @@<br>
> +//===-- compute_size_class_config.cpp ------------------------------<br>
> -------===//<br>
> +//<br>
> +// Part of the LLVM Project, under the Apache License v2.0 with LLVM<br>
> Exceptions.<br>
> +// See <br>
> <a href="https://protect2.fireeye.com/v1/url?k=d956bf68-85ddb452-d956fff3-86925ec6fd56-251f22b36b148136&q=1&e=3f260b55-66e1-4cd6-b863-a62c9b2a7eef&u=https%3A%2F%2Fllvm.org%2FLICENSE.txt" rel="noreferrer" target="_blank">https://protect2.fireeye.com/v1/url?k=d956bf68-85ddb452-d956fff3-86925ec6fd56-251f22b36b148136&q=1&e=3f260b55-66e1-4cd6-b863-a62c9b2a7eef&u=https%3A%2F%2Fllvm.org%2FLICENSE.txt</a><br>
>  for license information.<br>
> +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception<br>
> +//<br>
> +//===---------------------------------------------------------------<br>
> -------===//<br>
> +<br>
> +#include <errno.h><br>
> +#include <stdio.h><br>
> +#include <stdlib.h><br>
> +#include <string.h><br>
> +<br>
> +#include <algorithm><br>
> +#include <vector><br>
> +<br>
> +struct Alloc {<br>
> +  size_t size, count;<br>
> +};<br>
> +<br>
> +size_t measureWastage(const std::vector<Alloc> &allocs,<br>
> +                       const std::vector<size_t> &classes,<br>
> +                       size_t pageSize,<br>
> +                       size_t headerSize) {<br>
> +  size_t totalWastage = 0;<br>
> +  for (auto &a : allocs) {<br>
> +    size_t sizePlusHeader = a.size + headerSize;<br>
> +    size_t wastage = -1ull;<br>
> +    for (auto c : classes)<br>
> +      if (c >= sizePlusHeader && c - sizePlusHeader < wastage)<br>
> +        wastage = c - sizePlusHeader;<br>
> +    if (wastage == -1ull)<br>
> +      continue;<br>
> +    if (wastage > 2 * pageSize)<br>
> +      wastage = 2 * pageSize;<br>
> +    totalWastage += wastage * a.count;<br>
> +  }<br>
> +  return totalWastage;<br>
> +}<br>
> +<br>
> +void readAllocs(std::vector<Alloc> &allocs, const char *path) {<br>
> +  FILE *f = fopen(path, "r");<br>
> +  if (!f) {<br>
> +    fprintf(stderr, "compute_size_class_config: could not open %s:<br>
> %s\n", path,<br>
> +            strerror(errno));<br>
> +    exit(1);<br>
> +  }<br>
> +<br>
> +  const char header[] = "<malloc version=\"scudo-1\">\n";<br>
> +  char buf[sizeof(header) - 1];<br>
> +  if (fread(buf, 1, sizeof(header) - 1, f) != sizeof(header) - 1 ||<br>
> +      memcmp(buf, header, sizeof(header) - 1) != 0) {<br>
> +    fprintf(stderr, "compute_size_class_config: invalid input<br>
> format\n");<br>
> +    exit(1);<br>
> +  }<br>
> +<br>
> +  Alloc a;<br>
> +  while (fscanf(f, "<alloc size=\"%zu\" count=\"%zu\"/>\n", &a.size,<br>
> &a.count) == 2)<br>
> +    allocs.push_back(a);<br>
> +  fclose(f);<br>
> +}<br>
> +<br>
> +size_t log2Floor(size_t x) { return sizeof(long) * 8 - 1 -<br>
> __builtin_clzl(x); }<br>
> +<br>
> +void usage() {<br>
> +  fprintf(stderr,<br>
> +          "usage: compute_size_class_config [-p pageSize] [-c<br>
> largestClass] "<br>
> +          "[-h headerSize] [-n numClasses] [-b numBits]<br>
> profile...\n");<br>
> +  exit(1);<br>
> +}<br>
> +<br>
> +int main(int argc, char **argv) {<br>
> +  size_t pageSize = 4096;<br>
> +  size_t largestClass = 65552;<br>
> +  size_t headerSize = 16;<br>
> +  size_t numClasses = 32;<br>
> +  size_t numBits = 5;<br>
> +<br>
> +  std::vector<Alloc> allocs;<br>
> +  for (size_t i = 1; i != argc;) {<br>
> +    auto matchArg = [&](size_t &arg, const char *name) {<br>
> +      if (strcmp(argv[i], name) == 0) {<br>
> +        if (i + 1 != argc) {<br>
> +          arg = atoi(argv[i + 1]);<br>
> +          i += 2;<br>
> +        } else {<br>
> +          usage();<br>
> +        }<br>
> +        return true;<br>
> +      }<br>
> +      return false;<br>
> +    };<br>
> +    if (matchArg(pageSize, "-p") || matchArg(largestClass, "-c") ||<br>
> +        matchArg(headerSize, "-h") || matchArg(numClasses, "-n") ||<br>
> +        matchArg(numBits, "-b"))<br>
> +      continue;<br>
> +    readAllocs(allocs, argv[i]);<br>
> +    ++i;<br>
> +  }<br>
> +<br>
> +  if (allocs.empty())<br>
> +    usage();<br>
> +<br>
> +  std::vector<size_t> classes;<br>
> +  classes.push_back(largestClass);<br>
> +  for (size_t i = 1; i != numClasses; ++i) {<br>
> +    size_t minWastage = -1ull;<br>
> +    size_t minWastageClass;<br>
> +    for (size_t newClass = 16; newClass != largestClass; newClass +=<br>
> 16) {<br>
> +      // Skip classes with more than numBits bits, ignoring leading<br>
> or trailing<br>
> +      // zero bits.<br>
> +      if (__builtin_ctzl(newClass - headerSize) +<br>
> +              __builtin_clzl(newClass - headerSize) <<br>
> +          sizeof(long) * 8 - numBits)<br>
> +        continue;<br>
> +<br>
> +      classes.push_back(newClass);<br>
> +      size_t newWastage = measureWastage(allocs, classes, pageSize,<br>
> headerSize);<br>
> +      classes.pop_back();<br>
> +      if (newWastage < minWastage) {<br>
> +        minWastage = newWastage;<br>
> +        minWastageClass = newClass;<br>
> +      }<br>
> +    }<br>
> +    classes.push_back(minWastageClass);<br>
> +  }<br>
> +<br>
> +  std::sort(classes.begin(), classes.end());<br>
> +  size_t minSizeLog = log2Floor(headerSize);<br>
> +  size_t midSizeIndex = 0;<br>
> +  while (classes[midSizeIndex + 1] - classes[midSizeIndex] == (1 <<<br>
> minSizeLog))<br>
> +    midSizeIndex++;<br>
> +  size_t midSizeLog = log2Floor(classes[midSizeIndex] - headerSize);<br>
> +  size_t maxSizeLog = log2Floor(classes.back() - headerSize - 1) +<br>
> 1;<br>
> +<br>
> +  printf(R"(// wastage = %zu<br>
> +<br>
> +struct MySizeClassConfig {<br>
> +  static const uptr NumBits = %zu;<br>
> +  static const uptr MinSizeLog = %zu;<br>
> +  static const uptr MidSizeLog = %zu;<br>
> +  static const uptr MaxSizeLog = %zu;<br>
> +  static const u32 MaxNumCachedHint = 14;<br>
> +  static const uptr MaxBytesCachedLog = 14;<br>
> +<br>
> +  static constexpr u32 Classes[] = {)",<br>
> +         measureWastage(allocs, classes, pageSize, headerSize),<br>
> numBits,<br>
> +         minSizeLog, midSizeLog, maxSizeLog);<br>
> +  for (size_t i = 0; i != classes.size(); ++i) {<br>
> +    if ((i % 8) == 0)<br>
> +      printf("\n      ");<br>
> +    else<br>
> +      printf(" ");<br>
> +    printf("0x%05zx,", classes[i]);<br>
> +  }<br>
> +  printf(R"(<br>
> +  };<br>
> +  static const uptr SizeDelta = %zu;<br>
> +};<br>
> +)", headerSize);<br>
> +}<br>
> <br>
> <br>
>         <br>
> _______________________________________________<br>
> llvm-commits mailing list<br>
> <a href="mailto:llvm-commits@lists.llvm.org" target="_blank">llvm-commits@lists.llvm.org</a><br>
> <br>
<a href="https://protect2.fireeye.com/v1/url?k=d021f1fa-8caafac0-d021b161-86925ec6fd56-3a42fefbfe1ec5da&q=1&e=3f260b55-66e1-4cd6-b863-a62c9b2a7eef&u=https%3A%2F%2Flists.llvm.org%2Fcgi-bin%2Fmailman%2Flistinfo%2Fllvm-commits" rel="noreferrer" target="_blank">https://protect2.fireeye.com/v1/url?k=d021f1fa-8caafac0-d021b161-86925ec6fd56-3a42fefbfe1ec5da&q=1&e=3f260b55-66e1-4cd6-b863-a62c9b2a7eef&u=https%3A%2F%2Flists.llvm.org%2Fcgi-bin%2Fmailman%2Flistinfo%2Fllvm-commits</a><br>
_______________________________________________<br>
llvm-commits mailing list<br>
<a href="mailto:llvm-commits@lists.llvm.org" target="_blank">llvm-commits@lists.llvm.org</a><br>
<a href="https://lists.llvm.org/cgi-bin/mailman/listinfo/llvm-commits" rel="noreferrer" target="_blank">https://lists.llvm.org/cgi-bin/mailman/listinfo/llvm-commits</a><br>
</blockquote></div><br clear="all"><div><br></div>-- <br><div dir="ltr" class="gmail_signature"><div dir="ltr"><div><span style="font-family:Times;font-size:medium"><table cellspacing="0" cellpadding="0"><tbody><tr style="color:rgb(85,85,85);font-family:sans-serif;font-size:small"><td nowrap style="border-top:2px solid rgb(213,15,37)">Teresa Johnson |</td><td nowrap style="border-top:2px solid rgb(51,105,232)"> Software Engineer |</td><td nowrap style="border-top:2px solid rgb(0,153,57)"> <a href="mailto:tejohnson@google.com" target="_blank">tejohnson@google.com</a> |</td><td nowrap style="border-top:2px solid rgb(238,178,17)"><br></td></tr></tbody></table></span></div></div></div></div>