[Arm64] GetLargestOnDieCacheSize (#13071)
authorSteve MacLean <sdmaclea.qdt@qualcommdatacenter.com>
Wed, 2 Aug 2017 10:48:14 +0000 (06:48 -0400)
committerJan Vorlicek <janvorli@microsoft.com>
Wed, 2 Aug 2017 10:48:14 +0000 (12:48 +0200)
Look for cache info in /sys/.../cache/index*/size

If that fails estimate cache size based on processor count

src/gc/gc.cpp
src/pal/src/include/pal/palinternal.h
src/pal/src/misc/cgroup.cpp
src/pal/src/misc/sysinfo.cpp
src/vm/util.cpp

index 5afc024090d07e04ad6a4b6cbc6775a918ae3e94..24624d65c6cfa782e8e7f9a206e45d6f21bcdd3d 100644 (file)
@@ -35480,7 +35480,7 @@ size_t GCHeap::GetValidGen0MaxSize(size_t seg_size)
         // performance data seems to indicate halving the size results
         // in optimal perf.  Ask for adjusted gen0 size.
         gen0size = max(GCToOSInterface::GetLargestOnDieCacheSize(FALSE)/GCToOSInterface::GetLogicalCpuCount(),(256*1024));
-#if (defined(_TARGET_AMD64_))
+
         // if gen0 size is too large given the available memory, reduce it.
         // Get true cache size, as we don't want to reduce below this.
         size_t trueSize = max(GCToOSInterface::GetLargestOnDieCacheSize(TRUE)/GCToOSInterface::GetLogicalCpuCount(),(256*1024));
@@ -35500,8 +35500,6 @@ size_t GCHeap::GetValidGen0MaxSize(size_t seg_size)
                 break;
             }
         }
-#endif //_TARGET_AMD64_
-
 #else //SERVER_GC
         gen0size = max((4*GCToOSInterface::GetLargestOnDieCacheSize(TRUE)/5),(256*1024));
 #endif //SERVER_GC
index 48e2f3c683b0f2b463a1751de5b60f8c1e1f96e0..12312a30183fff0ab3d75717d520487a880f1f48 100644 (file)
@@ -637,6 +637,9 @@ typedef enum _TimeConversionConstants
 #ifdef __cplusplus
 }
 
+bool
+ReadMemoryValueFromFile(const char* filename, size_t* val);
+
 /* This is duplicated in utilcode.h for CLR, with cooler type-traits */
 template <typename T>
 inline
index b50ac86e4d48cefbf96b462d07b9d02761dc91e9..46d83842defaa05ffa8b883fd33dc159adb8ad63 100644 (file)
@@ -304,47 +304,7 @@ private:
 
     bool ReadMemoryValueFromFile(const char* filename, size_t* val)
     {
-        bool result = false;
-        char *line = nullptr;
-        size_t lineLen = 0;
-        char* endptr = nullptr;
-        size_t num = 0, l, multiplier;
-
-        if (val == nullptr)
-            return false;
-
-        FILE* file = fopen(filename, "r");
-        if (file == nullptr)
-            goto done;
-        
-        if (getline(&line, &lineLen, file) == -1)
-            goto done;
-
-        errno = 0;
-        num = strtoull(line, &endptr, 0); 
-        if (errno != 0)
-            goto done;
-
-        multiplier = 1;
-        switch(*endptr)
-        {
-            case 'g':
-            case 'G': multiplier = 1024;
-            case 'm': 
-            case 'M': multiplier = multiplier*1024;
-            case 'k':
-            case 'K': multiplier = multiplier*1024;
-        }
-
-        *val = num * multiplier;
-        result = true;
-        if (*val/multiplier != num)
-            result = false;
-    done:
-        if (file)
-            fclose(file);
-        free(line);    
-        return result;
+        return ::ReadMemoryValueFromFile(filename, val);
     }
 
     long long ReadCpuCGroupValue(const char* subsystemFilename){
index e1785688dc0e7829908914283deb1ab3aceb9ef6..a06f4b75cb9c6d3c89d564aeb2c7dcbb7f439c44 100644 (file)
@@ -384,6 +384,52 @@ PAL_HasGetCurrentProcessorNumber()
     return HAVE_SCHED_GETCPU;
 }
 
+bool
+ReadMemoryValueFromFile(const char* filename, size_t* val)
+{
+    bool result = false;
+    char *line = nullptr;
+    size_t lineLen = 0;
+    char* endptr = nullptr;
+    size_t num = 0, l, multiplier;
+
+    if (val == nullptr)
+        return false;
+
+    FILE* file = fopen(filename, "r");
+    if (file == nullptr)
+        goto done;
+
+    if (getline(&line, &lineLen, file) == -1)
+        goto done;
+
+    errno = 0;
+    num = strtoull(line, &endptr, 0);
+    if (errno != 0)
+        goto done;
+
+    multiplier = 1;
+    switch(*endptr)
+    {
+        case 'g':
+        case 'G': multiplier = 1024;
+        case 'm':
+        case 'M': multiplier = multiplier*1024;
+        case 'k':
+        case 'K': multiplier = multiplier*1024;
+    }
+
+    *val = num * multiplier;
+    result = true;
+    if (*val/multiplier != num)
+        result = false;
+done:
+    if (file)
+        fclose(file);
+    free(line);
+    return result;
+}
+
 size_t
 PALAPI
 PAL_GetLogicalProcessorCacheSizeFromOS()
@@ -403,5 +449,48 @@ PAL_GetLogicalProcessorCacheSizeFromOS()
     cacheSize = max(cacheSize, sysconf(_SC_LEVEL4_CACHE_SIZE));
 #endif
 
+#if defined(_ARM64_)
+    if(cacheSize == 0)
+    {
+        size_t size;
+
+        if(ReadMemoryValueFromFile("/sys/devices/system/cpu/cpu0/cache/index0/size", &size))
+            cacheSize = max(cacheSize, size);
+        if(ReadMemoryValueFromFile("/sys/devices/system/cpu/cpu0/cache/index1/size", &size))
+            cacheSize = max(cacheSize, size);
+        if(ReadMemoryValueFromFile("/sys/devices/system/cpu/cpu0/cache/index2/size", &size))
+            cacheSize = max(cacheSize, size);
+        if(ReadMemoryValueFromFile("/sys/devices/system/cpu/cpu0/cache/index3/size", &size))
+            cacheSize = max(cacheSize, size);
+        if(ReadMemoryValueFromFile("/sys/devices/system/cpu/cpu0/cache/index4/size", &size))
+            cacheSize = max(cacheSize, size);
+    }
+
+    if(cacheSize == 0)
+    {
+        // It is currently expected to be missing cache size info
+        //
+        // _SC_LEVEL*_*CACHE_SIZE is not yet present.  Work is in progress to enable this for arm64
+        //
+        // /sys/devices/system/cpu/cpu*/cache/index*/ is also not yet present in most systems.
+        // Arm64 patch is in Linux kernel tip.
+        //
+        // midr_el1 is available in "/sys/devices/system/cpu/cpu0/regs/identification/midr_el1",
+        // but without an exhaustive list of ARM64 processors any decode of midr_el1
+        // Would likely be incomplete
+
+        // Published information on ARM64 architectures is limited.
+        // If we use recent high core count chips as a guide for state of the art, we find
+        // total L3 cache to be 1-2MB/core.  As always, there are exceptions.
+
+        // Estimate cache size based on CPU count
+        // Assume lower core count are lighter weight parts which are likely to have smaller caches
+        // Assume L3$/CPU grows linearly from 256K to 1.5M/CPU as logicalCPUs grows from 2 to 12 CPUs
+        DWORD logicalCPUs = PAL_GetLogicalCpuCountFromOS();
+
+        cacheSize = logicalCPUs*min(1536, max(256, logicalCPUs*128))*1024;
+    }
+#endif
+
     return cacheSize;
 }
index 724536a231c4f606b4dd4d29a329a5bd8edd58d4..12c3cea83a7c789da73e1248a09a5ef31656e37e 100644 (file)
@@ -1861,8 +1861,6 @@ size_t GetLargestOnDieCacheSize(BOOL bTrueSize)
     STATIC_CONTRACT_NOTHROW;
     STATIC_CONTRACT_GC_NOTRIGGER;
 
-#if defined(_TARGET_AMD64_) || defined (_TARGET_X86_)
-
     static size_t maxSize;
     static size_t maxTrueSize;
 
@@ -1879,6 +1877,7 @@ size_t GetLargestOnDieCacheSize(BOOL bTrueSize)
         }
     }
 
+#if defined(_TARGET_AMD64_) || defined (_TARGET_X86_)
     DefaultCatchFilterParam param;
     param.pv = COMPLUS_EXCEPTION_EXECUTE_HANDLER;
 
@@ -2001,18 +2000,20 @@ size_t GetLargestOnDieCacheSize(BOOL bTrueSize)
     {
     }
     PAL_ENDTRY
+#else
+    maxSize = maxTrueSize = GetLogicalProcessorCacheSizeFromOS() ; // Returns the size of the highest level processor cache
+#endif
+
+#if defined(_TARGET_ARM64_)
+    // Bigger gen0 size helps arm64 targets
+    maxSize = maxTrueSize * 3;
+#endif
 
     //    printf("GetLargestOnDieCacheSize returns %d, adjusted size %d\n", maxSize, maxTrueSize);
     if (bTrueSize)
         return maxTrueSize;
     else
         return maxSize;
-
-#else
-    size_t cache_size = GetLogicalProcessorCacheSizeFromOS() ; // Returns the size of the highest level processor cache
-    return cache_size;
-
-#endif
 }
 
 //---------------------------------------------------------------------