[iOS] Use memory footprint to dynamically adjust behavior of allocators
authormsaboff@apple.com <msaboff@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Fri, 12 May 2017 14:15:08 +0000 (14:15 +0000)
committermsaboff@apple.com <msaboff@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Fri, 12 May 2017 14:15:08 +0000 (14:15 +0000)
https://bugs.webkit.org/show_bug.cgi?id=171944

Reviewed by Filip Pizlo.

Source/bmalloc:

This change is iOS only.

After the scavenger thread completes scavenging, it asks the OS for how much total memory the
process is using.  This information is used to update the sleep delay for the scanvenger thread,
as well as to provide memory in use data for other parts of the system.

The scavenger sleep time is calculated using the following quadradic equation.

    scavengerSleep = 1.2*percentFreeMemory^2 - percentFreeMemory + 2

Where percentFreeMemory is between 0 and 100.  The result is constrained to the values 2 and 250.

This equation empirically works out to providing a 2ms sleep time when we have less than 10%
memory available, 30ms when 20% is available and 250ms when 50% or more is available.  In testing,
this exponentially agressive scavenging delay by itself reduced memory usage and made it much
more deterministic when used without the corresponding change in the JSC Heap.

Changed the scavenger thread to use the User Initiated QOS priority to ensure it doesn't
get starved.

Moved the non-Windows functionality of WTF::RAMSize() to new files AvailableMemory.{cpp,h}
and implemented in the function availableMemory().  That functions limits the value returned
on iOS to a maximum of 840MB as that is the jetsam soft memory limit.
Added a new API availableMemory() so that WTF::RAMSize() will use this value.

* CMakeLists.txt:
* bmalloc.xcodeproj/project.pbxproj:
* bmalloc/BPlatform.h:
* bmalloc/Heap.cpp:
(bmalloc::Heap::Heap):
(bmalloc::Heap::updateMemoryInUseParameters):
(bmalloc::Heap::concurrentScavenge):
(bmalloc::Heap::scavenge):
* bmalloc/Heap.h:
(bmalloc::Heap::memoryFootprint):
(bmalloc::Heap::percentAvailableMemoryInUse):
* bmalloc/Sizes.h:
* bmalloc/bmalloc.h:
(bmalloc::api::availableMemory):
(bmalloc::api::memoryFootprint):
(bmalloc::api::percentAvailableMemoryInUse):
* bmalloc/AvailableMemory.cpp: Added.
(bmalloc::computeAvailableMemory):
(bmalloc::availableMemory):
* bmalloc/AvailableMemory.h: Added.

Source/JavaScriptCore:

This change is iOS only.

Added the ability to react to when memory usage is critical.  This is defined as memory
usage being above the newly added option criticalGCMemoryThreshold.  When we are in this
critical state, all collections are Full and we limit the amount of memory we allocate
between collections to 1/4th the memory above the critical threshold.

Changed the calculation of proportionalHeapSize to be based on process memory footprint
and not how big the heap is.  Also, the values of Options::smallHeapRAMFraction and
Options::mediumHeapRAMFraction are overriden so that most of the heap growth is happens
using the more agressive Options::smallHeapGrowthFactor.

* heap/Heap.cpp:
(JSC::Heap::Heap):
(JSC::Heap::overCriticalMemoryThreshold):
(JSC::Heap::shouldDoFullCollection):
(JSC::Heap::collectIfNecessaryOrDefer):
* heap/Heap.h:
* runtime/Options.cpp:
(JSC::overrideDefaults):
(JSC::Options::initialize):
* runtime/Options.h:

Source/WTF:

Moved the non-Windows implementation of RAMSize() to bmalloc/AvailableMemory.cpp and
called the function availableMemory().

* wtf/RAMSize.cpp:
(WTF::computeRAMSize):

git-svn-id: https://svn.webkit.org/repository/webkit/trunk@216763 268f45cc-cd09-0410-ab3c-d52691b4dbfc

17 files changed:
Source/JavaScriptCore/ChangeLog
Source/JavaScriptCore/heap/Heap.cpp
Source/JavaScriptCore/heap/Heap.h
Source/JavaScriptCore/runtime/Options.cpp
Source/JavaScriptCore/runtime/Options.h
Source/WTF/ChangeLog
Source/WTF/wtf/RAMSize.cpp
Source/bmalloc/CMakeLists.txt
Source/bmalloc/ChangeLog
Source/bmalloc/bmalloc.xcodeproj/project.pbxproj
Source/bmalloc/bmalloc/AvailableMemory.cpp [new file with mode: 0644]
Source/bmalloc/bmalloc/AvailableMemory.h [new file with mode: 0644]
Source/bmalloc/bmalloc/BPlatform.h
Source/bmalloc/bmalloc/Heap.cpp
Source/bmalloc/bmalloc/Heap.h
Source/bmalloc/bmalloc/Sizes.h
Source/bmalloc/bmalloc/bmalloc.h

index a06087b..11dc662 100644 (file)
@@ -1,3 +1,33 @@
+2017-05-12  Michael Saboff  <msaboff@apple.com>
+
+        [iOS] Use memory footprint to dynamically adjust behavior of allocators
+        https://bugs.webkit.org/show_bug.cgi?id=171944
+
+        Reviewed by Filip Pizlo.
+
+        This change is iOS only.
+
+        Added the ability to react to when memory usage is critical.  This is defined as memory
+        usage being above the newly added option criticalGCMemoryThreshold.  When we are in this
+        critical state, all collections are Full and we limit the amount of memory we allocate
+        between collections to 1/4th the memory above the critical threshold.
+
+        Changed the calculation of proportionalHeapSize to be based on process memory footprint
+        and not how big the heap is.  Also, the values of Options::smallHeapRAMFraction and
+        Options::mediumHeapRAMFraction are overriden so that most of the heap growth is happens
+        using the more agressive Options::smallHeapGrowthFactor.
+
+        * heap/Heap.cpp:
+        (JSC::Heap::Heap):
+        (JSC::Heap::overCriticalMemoryThreshold):
+        (JSC::Heap::shouldDoFullCollection):
+        (JSC::Heap::collectIfNecessaryOrDefer):
+        * heap/Heap.h:
+        * runtime/Options.cpp:
+        (JSC::overrideDefaults):
+        (JSC::Options::initialize):
+        * runtime/Options.h:
+
 2017-05-11  Saam Barati  <sbarati@apple.com>
 
         Computing optionalDefArgWidth in CheckSpecial should not consider Scratch roles
index 7d2ed3c..921da7b 100644 (file)
@@ -68,6 +68,9 @@
 #include "WasmMemory.h"
 #include "WeakSetInlines.h"
 #include <algorithm>
+#if PLATFORM(IOS)
+#include <bmalloc/bmalloc.h>
+#endif
 #include <wtf/CurrentTime.h>
 #include <wtf/MainThread.h>
 #include <wtf/ParallelVectorIterator.h>
@@ -112,10 +115,18 @@ size_t minHeapSize(HeapType heapType, size_t ramSize)
 
 size_t proportionalHeapSize(size_t heapSize, size_t ramSize)
 {
+#if PLATFORM(IOS)
+    size_t memoryFootprint = bmalloc::api::memoryFootprint();
+    if (memoryFootprint < ramSize * Options::smallHeapRAMFraction())
+        return Options::smallHeapGrowthFactor() * heapSize;
+    if (memoryFootprint < ramSize * Options::mediumHeapRAMFraction())
+        return Options::mediumHeapGrowthFactor() * heapSize;
+#else
     if (heapSize < ramSize * Options::smallHeapRAMFraction())
         return Options::smallHeapGrowthFactor() * heapSize;
     if (heapSize < ramSize * Options::mediumHeapRAMFraction())
         return Options::mediumHeapGrowthFactor() * heapSize;
+#endif
     return Options::largeHeapGrowthFactor() * heapSize;
 }
 
@@ -313,6 +324,10 @@ Heap::Heap(VM* vm, HeapType heapType)
     
     m_collectorSlotVisitor->optimizeForStoppedMutator();
 
+    // When memory is critical, allow allocating 25% of the amount above the critical threshold before collecting.
+    size_t memoryAboveCriticalThreshold = static_cast<size_t>(static_cast<double>(m_ramSize) * (1.0 - Options::criticalGCMemoryThreshold()));
+    m_maxEdenSizeWhenCritical = memoryAboveCriticalThreshold / 4;
+
     LockHolder locker(*m_threadLock);
     m_thread = adoptRef(new Thread(locker, *this));
 }
@@ -488,6 +503,16 @@ bool Heap::webAssemblyFastMemoriesThisCycleAtThreshold() const
     return m_webAssemblyFastMemoriesAllocatedThisCycle > fastMemoryThreshold;
 }
 
+bool Heap::overCriticalMemoryThreshold() const
+{
+#if PLATFORM(IOS)
+    if (bmalloc::api::percentAvailableMemoryInUse() > Options::criticalGCMemoryThreshold())
+        return true;
+#endif
+
+    return false;
+}
+
 void Heap::reportAbandonedObjectGraph()
 {
     // Our clients don't know exactly how much memory they
@@ -2339,7 +2364,7 @@ bool Heap::shouldDoFullCollection() const
         return true;
 
     if (!m_currentRequest.scope)
-        return m_shouldDoFullCollection || webAssemblyFastMemoriesThisCycleAtThreshold();
+        return m_shouldDoFullCollection || webAssemblyFastMemoriesThisCycleAtThreshold() || overCriticalMemoryThreshold();
     return *m_currentRequest.scope == CollectionScope::Full;
 }
 
@@ -2490,8 +2515,15 @@ void Heap::collectIfNecessaryOrDefer(GCDeferralContext* deferralContext)
         if (m_bytesAllocatedThisCycle <= Options::gcMaxHeapSize())
             return;
     } else {
+        size_t bytesAllowedThisCycle = m_maxEdenSize;
+
+#if PLATFORM(IOS)
+        if (overCriticalMemoryThreshold())
+            bytesAllowedThisCycle = std::min(m_maxEdenSizeWhenCritical, bytesAllowedThisCycle);
+#endif
+
         if (!webAssemblyFastMemoriesThisCycleAtThreshold()
-            && m_bytesAllocatedThisCycle <= m_maxEdenSize)
+            && m_bytesAllocatedThisCycle <= bytesAllowedThisCycle)
             return;
     }
 
index 62463b9..9c90edf 100644 (file)
@@ -219,6 +219,8 @@ public:
     void reportWebAssemblyFastMemoriesAllocated(size_t);
     bool webAssemblyFastMemoriesThisCycleAtThreshold() const;
 
+    bool overCriticalMemoryThreshold() const;
+    
 #if ENABLE(RESOURCE_USAGE)
     // Use this API to report the subset of extra memory that lives outside this process.
     JS_EXPORT_PRIVATE void reportExternalMemoryVisited(size_t);
@@ -553,6 +555,7 @@ private:
     size_t m_webAssemblyFastMemoriesAllocatedThisCycle;
     size_t m_bytesAbandonedSinceLastFullCollect;
     size_t m_maxEdenSize;
+    size_t m_maxEdenSizeWhenCritical;
     size_t m_maxHeapSize;
     bool m_shouldDoFullCollection;
     size_t m_totalBytesVisited;
index b04de35..3757786 100644 (file)
@@ -344,6 +344,10 @@ static void overrideDefaults()
     }
 
 #if PLATFORM(IOS)
+    // On iOS, we control heap growth using process memory footprint. Therefore these values can be agressive.
+    Options::smallHeapRAMFraction() = 0.8;
+    Options::mediumHeapRAMFraction() = 0.9;
+
     Options::useSigillCrashAnalyzer() = true;
 #endif
 
@@ -542,6 +546,7 @@ void Options::initialize()
             ASSERT(Options::thresholdForOptimizeAfterLongWarmUp() >= Options::thresholdForOptimizeAfterWarmUp());
             ASSERT(Options::thresholdForOptimizeAfterWarmUp() >= Options::thresholdForOptimizeSoon());
             ASSERT(Options::thresholdForOptimizeAfterWarmUp() >= 0);
+            ASSERT(Options::criticalGCMemoryThreshold() > 0.0 && Options::criticalGCMemoryThreshold() < 1.0);
 
             dumpOptionsIfNeeded();
             ensureOptionsAreCoherent();
index cf9dc2b..728a273 100644 (file)
@@ -206,6 +206,7 @@ typedef const char* optionString;
     v(double, mediumHeapRAMFraction, 0.5, Normal, nullptr) \
     v(double, mediumHeapGrowthFactor, 1.5, Normal, nullptr) \
     v(double, largeHeapGrowthFactor, 1.24, Normal, nullptr) \
+    v(double, criticalGCMemoryThreshold, 0.88, Normal, "percent memory in use the GC considers critical.  The collector is much more aggressive above this threshold") \
     v(double, minimumMutatorUtilization, 0, Normal, nullptr) \
     v(double, maximumMutatorUtilization, 0.7, Normal, nullptr) \
     v(double, epsilonMutatorUtilization, 0.01, Normal, nullptr) \
index ddab49d..5ec7752 100644 (file)
@@ -1,3 +1,16 @@
+2017-05-12  Michael Saboff  <msaboff@apple.com>
+
+        [iOS] Use memory footprint to dynamically adjust behavior of allocators
+        https://bugs.webkit.org/show_bug.cgi?id=171944
+
+        Reviewed by Filip Pizlo.
+
+        Moved the non-Windows implementation of RAMSize() to bmalloc/AvailableMemory.cpp and
+        called the function availableMemory().
+
+        * wtf/RAMSize.cpp:
+        (WTF::computeRAMSize):
+
 2017-05-12  Claudio Saavedra  <csaavedra@igalia.com>
 
         [WPE] Add MemoryFootprintLinux to build
index deb69e1..b017a26 100644 (file)
 #include "StdLibExtras.h"
 #include <mutex>
 
-#if OS(DARWIN)
-#import <dispatch/dispatch.h>
-#import <mach/host_info.h>
-#import <mach/mach.h>
-#import <mach/mach_error.h>
-#import <math.h>
-#elif OS(UNIX)
-#include <unistd.h>
-#elif OS(WINDOWS)
+#if OS(WINDOWS)
 #include <windows.h>
+#else
+#include <bmalloc/bmalloc.h>
 #endif
 
 namespace WTF {
-
+    
+#if OS(WINDOWS)
 static const size_t ramSizeGuess = 512 * MB;
+#endif
 
 static size_t computeRAMSize()
 {
-#if PLATFORM(IOS_SIMULATOR)
-    // Pretend we have 512MB of memory to make cache sizes behave like on device.
-    return ramSizeGuess;
-#elif OS(DARWIN)
-    host_basic_info_data_t hostInfo;
-
-    mach_port_t host = mach_host_self();
-    mach_msg_type_number_t count = HOST_BASIC_INFO_COUNT;
-    kern_return_t r = host_info(host, HOST_BASIC_INFO, (host_info_t)&hostInfo, &count);
-    mach_port_deallocate(mach_task_self(), host);
-    if (r != KERN_SUCCESS) {
-        LOG_ERROR("%s : host_info(%d) : %s.\n", __FUNCTION__, r, mach_error_string(r));
-        return ramSizeGuess;
-    }
-
-    if (hostInfo.max_mem > std::numeric_limits<size_t>::max())
-        return std::numeric_limits<size_t>::max();
-
-    size_t sizeAccordingToKernel = static_cast<size_t>(hostInfo.max_mem);
-    size_t multiple = 128 * MB;
-
-    // Round up the memory size to a multiple of 128MB because max_mem may not be exactly 512MB
-    // (for example) and we have code that depends on those boundaries.
-    return ((sizeAccordingToKernel + multiple - 1) / multiple) * multiple;
-#elif OS(UNIX)
-    long pages = sysconf(_SC_PHYS_PAGES);
-    long pageSize = sysconf(_SC_PAGE_SIZE);
-    if (pages == -1 || pageSize == -1)
-        return ramSizeGuess;
-    return pages * pageSize;
-#elif OS(WINDOWS)
+#if OS(WINDOWS)
     MEMORYSTATUSEX status;
     status.dwLength = sizeof(status);
     bool result = GlobalMemoryStatusEx(&status);
     if (!result)
         return ramSizeGuess;
     return status.ullTotalPhys;
+#else
+    return bmalloc::api::availableMemory();
 #endif
 }
 
index 7e3139a..80e135c 100644 (file)
@@ -6,6 +6,7 @@ set(bmalloc_INCLUDE_DIRECTORIES
 
 set(bmalloc_SOURCES
     bmalloc/Allocator.cpp
+    bmalloc/AvailableMemory.cpp
     bmalloc/Cache.cpp
     bmalloc/Deallocator.cpp
     bmalloc/DebugHeap.cpp
index 6d29709..d5a7fa0 100644 (file)
@@ -1,3 +1,56 @@
+2017-05-12  Michael Saboff  <msaboff@apple.com>
+
+        [iOS] Use memory footprint to dynamically adjust behavior of allocators
+        https://bugs.webkit.org/show_bug.cgi?id=171944
+
+        Reviewed by Filip Pizlo.
+
+        This change is iOS only.
+
+        After the scavenger thread completes scavenging, it asks the OS for how much total memory the
+        process is using.  This information is used to update the sleep delay for the scanvenger thread,
+        as well as to provide memory in use data for other parts of the system.
+
+        The scavenger sleep time is calculated using the following quadradic equation.
+
+            scavengerSleep = 1.2*percentFreeMemory^2 - percentFreeMemory + 2
+
+        Where percentFreeMemory is between 0 and 100.  The result is constrained to the values 2 and 250.
+
+        This equation empirically works out to providing a 2ms sleep time when we have less than 10%
+        memory available, 30ms when 20% is available and 250ms when 50% or more is available.  In testing,
+        this exponentially agressive scavenging delay by itself reduced memory usage and made it much
+        more deterministic when used without the corresponding change in the JSC Heap.
+
+        Changed the scavenger thread to use the User Initiated QOS priority to ensure it doesn't
+        get starved.
+
+        Moved the non-Windows functionality of WTF::RAMSize() to new files AvailableMemory.{cpp,h}
+        and implemented in the function availableMemory().  That functions limits the value returned
+        on iOS to a maximum of 840MB as that is the jetsam soft memory limit.
+        Added a new API availableMemory() so that WTF::RAMSize() will use this value.
+
+        * CMakeLists.txt:
+        * bmalloc.xcodeproj/project.pbxproj:
+        * bmalloc/BPlatform.h:
+        * bmalloc/Heap.cpp:
+        (bmalloc::Heap::Heap):
+        (bmalloc::Heap::updateMemoryInUseParameters):
+        (bmalloc::Heap::concurrentScavenge):
+        (bmalloc::Heap::scavenge):
+        * bmalloc/Heap.h:
+        (bmalloc::Heap::memoryFootprint):
+        (bmalloc::Heap::percentAvailableMemoryInUse):
+        * bmalloc/Sizes.h:
+        * bmalloc/bmalloc.h:
+        (bmalloc::api::availableMemory):
+        (bmalloc::api::memoryFootprint):
+        (bmalloc::api::percentAvailableMemoryInUse):
+        * bmalloc/AvailableMemory.cpp: Added.
+        (bmalloc::computeAvailableMemory):
+        (bmalloc::availableMemory):
+        * bmalloc/AvailableMemory.h: Added.
+
 2017-05-05  Joseph Pecoraro  <pecoraro@apple.com>
 
         Leaks always reports "WebKit Malloc Memory Pressure Handler" dispatch_queue/source as leaking
index 5fe52ba..aa02086 100644 (file)
@@ -60,6 +60,8 @@
                4426E2801C838EE0008EB042 /* Logging.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4426E27E1C838EE0008EB042 /* Logging.cpp */; };
                4426E2811C838EE0008EB042 /* Logging.h in Headers */ = {isa = PBXBuildFile; fileRef = 4426E27F1C838EE0008EB042 /* Logging.h */; settings = {ATTRIBUTES = (Private, ); }; };
                4426E2831C839547008EB042 /* BSoftLinking.h in Headers */ = {isa = PBXBuildFile; fileRef = 4426E2821C839547008EB042 /* BSoftLinking.h */; };
+               6599C5CC1EC3F15900A2F7BB /* AvailableMemory.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 6599C5CA1EC3F15900A2F7BB /* AvailableMemory.cpp */; };
+               6599C5CD1EC3F15900A2F7BB /* AvailableMemory.h in Headers */ = {isa = PBXBuildFile; fileRef = 6599C5CB1EC3F15900A2F7BB /* AvailableMemory.h */; settings = {ATTRIBUTES = (Private, ); }; };
 /* End PBXBuildFile section */
 
 /* Begin PBXContainerItemProxy section */
                4426E27E1C838EE0008EB042 /* Logging.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = Logging.cpp; path = bmalloc/Logging.cpp; sourceTree = "<group>"; };
                4426E27F1C838EE0008EB042 /* Logging.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = Logging.h; path = bmalloc/Logging.h; sourceTree = "<group>"; };
                4426E2821C839547008EB042 /* BSoftLinking.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = BSoftLinking.h; path = bmalloc/darwin/BSoftLinking.h; sourceTree = "<group>"; };
+               6599C5CA1EC3F15900A2F7BB /* AvailableMemory.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = AvailableMemory.cpp; path = bmalloc/AvailableMemory.cpp; sourceTree = "<group>"; };
+               6599C5CB1EC3F15900A2F7BB /* AvailableMemory.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = AvailableMemory.h; path = bmalloc/AvailableMemory.h; sourceTree = "<group>"; };
 /* End PBXFileReference section */
 
 /* Begin PBXFrameworksBuildPhase section */
                                4408F2961C9896C40012EC64 /* darwin */,
                                1421A87718EE462A00B4DD68 /* Algorithm.h */,
                                1417F65218BA88A00076FA3F /* AsyncTask.h */,
+                               6599C5CA1EC3F15900A2F7BB /* AvailableMemory.cpp */,
+                               6599C5CB1EC3F15900A2F7BB /* AvailableMemory.h */,
                                1413E468189EEDE400546D68 /* BAssert.h */,
                                14C919C818FCC59F0028DB43 /* BPlatform.h */,
                                14D9DB4517F2447100EAAB79 /* FixedVector.h */,
                                14DD78C518F48D7500950702 /* Algorithm.h in Headers */,
                                14DD789818F48D4A00950702 /* Allocator.h in Headers */,
                                14DD78C618F48D7500950702 /* AsyncTask.h in Headers */,
+                               6599C5CD1EC3F15900A2F7BB /* AvailableMemory.h in Headers */,
                                14DD78C718F48D7500950702 /* BAssert.h in Headers */,
                                1448C30118F3754C00502839 /* bmalloc.h in Headers */,
                                14C919C918FCC59F0028DB43 /* BPlatform.h in Headers */,
                                4426E2801C838EE0008EB042 /* Logging.cpp in Sources */,
                                142B44361E2839E7001DA6E9 /* DebugHeap.cpp in Sources */,
                                14F271C818EA3990008C152F /* ObjectType.cpp in Sources */,
+                               6599C5CC1EC3F15900A2F7BB /* AvailableMemory.cpp in Sources */,
                                143CB81C19022BC900B16A45 /* StaticMutex.cpp in Sources */,
                                14F271C918EA3990008C152F /* VMHeap.cpp in Sources */,
                                1440AFCD1A9527AF00837FAA /* Zone.cpp in Sources */,
diff --git a/Source/bmalloc/bmalloc/AvailableMemory.cpp b/Source/bmalloc/bmalloc/AvailableMemory.cpp
new file mode 100644 (file)
index 0000000..b1be113
--- /dev/null
@@ -0,0 +1,95 @@
+/*
+ * Copyright (C) 2012-2017 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "AvailableMemory.h"
+
+#include "Sizes.h"
+#include <mutex>
+#if BOS(DARWIN)
+#if BPLATFORM(IOS)
+#import <algorithm>
+#endif
+#import <dispatch/dispatch.h>
+#import <mach/host_info.h>
+#import <mach/mach.h>
+#import <mach/mach_error.h>
+#import <math.h>
+#elif BOS(UNIX)
+#include <unistd.h>
+#endif
+
+namespace bmalloc {
+
+static const size_t availableMemoryGuess = 512 * bmalloc::MB;
+
+static size_t computeAvailableMemory()
+{
+#if BPLATFORM(IOS_SIMULATOR)
+    // Pretend we have 512MB of memory to make cache sizes behave like on device.
+    return availableMemoryGuess;
+#elif BOS(DARWIN)
+    host_basic_info_data_t hostInfo;
+
+    mach_port_t host = mach_host_self();
+    mach_msg_type_number_t count = HOST_BASIC_INFO_COUNT;
+    kern_return_t r = host_info(host, HOST_BASIC_INFO, (host_info_t)&hostInfo, &count);
+    mach_port_deallocate(mach_task_self(), host);
+    if (r != KERN_SUCCESS)
+        return availableMemoryGuess;
+    
+    if (hostInfo.max_mem > std::numeric_limits<size_t>::max())
+        return std::numeric_limits<size_t>::max();
+
+    size_t sizeAccordingToKernel = static_cast<size_t>(hostInfo.max_mem);
+#if BPLATFORM(IOS)
+    sizeAccordingToKernel = std::min(sizeAccordingToKernel, 840 * bmalloc::MB);
+#endif
+    size_t multiple = 128 * bmalloc::MB;
+
+    // Round up the memory size to a multiple of 128MB because max_mem may not be exactly 512MB
+    // (for example) and we have code that depends on those boundaries.
+    return ((sizeAccordingToKernel + multiple - 1) / multiple) * multiple;
+#elif BOS(UNIX)
+    long pages = sysconf(_SC_PHYS_PAGES);
+    long pageSize = sysconf(_SC_PAGE_SIZE);
+    if (pages == -1 || pageSize == -1)
+        return availableMemoryGuess;
+    return pages * pageSize;
+#else
+    return availableMemoryGuess;
+#endif
+}
+
+size_t availableMemory()
+{
+    static size_t availableMemory;
+    static std::once_flag onceFlag;
+    std::call_once(onceFlag, [] {
+        availableMemory = computeAvailableMemory();
+    });
+    return availableMemory;
+}
+
+} // namespace bmalloc
diff --git a/Source/bmalloc/bmalloc/AvailableMemory.h b/Source/bmalloc/bmalloc/AvailableMemory.h
new file mode 100644 (file)
index 0000000..7c5ed89
--- /dev/null
@@ -0,0 +1,36 @@
+/*
+ * Copyright (C) 2012-2017 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#pragma once
+
+#include "BPlatform.h"
+#include "Sizes.h"
+
+namespace bmalloc {
+
+size_t availableMemory();
+
+}
+
index 8d768db..fe2e8ad 100644 (file)
@@ -46,6 +46,9 @@
     || (defined(TARGET_OS_IPHONE) && TARGET_OS_IPHONE) \
     || (defined(TARGET_IPHONE_SIMULATOR) && TARGET_IPHONE_SIMULATOR))
 #define BPLATFORM_IOS 1
+#if (defined(TARGET_IPHONE_SIMULATOR) && TARGET_IPHONE_SIMULATOR)
+#define BPLATFORM_IOS_SIMULATOR 1
+#endif
 #elif BOS(DARWIN) && defined(TARGET_OS_MAC) && TARGET_OS_MAC
 #define BPLATFORM_MAC 1
 #endif
index 5cd803b..55ba11f 100644 (file)
  */
 
 #include "Heap.h"
+
+#if BPLATFORM(IOS)
+#include "AvailableMemory.h"
+#endif
 #include "BumpAllocator.h"
 #include "Chunk.h"
 #include "DebugHeap.h"
 
 #if BOS(DARWIN)
 #include "bmalloc.h"
+#if BPLATFORM(IOS)
+#import <mach/host_info.h>
+#import <mach/mach.h>
+#import <mach/mach_error.h>
+#endif
 #endif
 
 namespace bmalloc {
@@ -42,6 +51,9 @@ Heap::Heap(std::lock_guard<StaticMutex>&)
     : m_vmPageSizePhysical(vmPageSizePhysical())
     , m_scavenger(*this, &Heap::concurrentScavenge)
     , m_debugHeap(nullptr)
+#if BPLATFORM(IOS)
+    , m_maxAvailableMemory(availableMemory())
+#endif
 {
     RELEASE_BASSERT(vmPageSizePhysical() >= smallPageSize);
     RELEASE_BASSERT(vmPageSize() >= vmPageSizePhysical());
@@ -119,6 +131,28 @@ void Heap::initializePageMetadata()
         m_pageClasses[i] = (computePageSize(i) - 1) / smallPageSize;
 }
 
+#if BPLATFORM(IOS)
+void Heap::updateMemoryInUseParameters()
+{
+    task_vm_info_data_t vmInfo;
+    mach_msg_type_number_t vmSize = TASK_VM_INFO_COUNT;
+    
+    if (KERN_SUCCESS != task_info(mach_task_self(), TASK_VM_INFO, (task_info_t)(&vmInfo), &vmSize))
+        m_memoryFootprint = 0;
+    else
+        m_memoryFootprint = static_cast<size_t>(vmInfo.phys_footprint);
+
+    double percentInUse = static_cast<double>(m_memoryFootprint) / static_cast<double>(m_maxAvailableMemory);
+    m_percentAvailableMemoryInUse = std::min(percentInUse, 1.0);
+
+    double percentFree = 1.0 - m_percentAvailableMemoryInUse;
+    double sleepInMS = 1200.0 * percentFree * percentFree - 100.0 * percentFree + 2.0;
+    sleepInMS = std::max(std::min(sleepInMS, static_cast<double>(maxScavengeSleepDuration.count())), 2.0);
+
+    m_scavengeSleepDuration = std::chrono::milliseconds(static_cast<long long>(sleepInMS));
+}
+#endif
+
 void Heap::concurrentScavenge()
 {
 #if BOS(DARWIN)
@@ -128,6 +162,10 @@ void Heap::concurrentScavenge()
     std::unique_lock<StaticMutex> lock(PerProcess<Heap>::mutex());
 
     scavenge(lock, Async);
+
+#if BPLATFORM(IOS)
+    updateMemoryInUseParameters();
+#endif
 }
 
 void Heap::scavenge(std::unique_lock<StaticMutex>& lock, ScavengeMode scavengeMode)
@@ -136,7 +174,7 @@ void Heap::scavenge(std::unique_lock<StaticMutex>& lock, ScavengeMode scavengeMo
     m_isAllocatingLargePages = false;
 
     if (scavengeMode == Async)
-        sleep(lock, scavengeSleepDuration);
+        sleep(lock, m_scavengeSleepDuration);
 
     scavengeSmallPages(lock, scavengeMode);
     scavengeLargeObjects(lock, scavengeMode);
index 7d41c02..df03308 100644 (file)
@@ -72,6 +72,11 @@ public:
 
     void scavenge(std::unique_lock<StaticMutex>&, ScavengeMode);
 
+#if BPLATFORM(IOS)
+    size_t memoryFootprint();
+    double percentAvailableMemoryInUse();
+#endif
+
 #if BOS(DARWIN)
     void setScavengerThreadQOSClass(qos_class_t overrideClass) { m_requestedScavengerThreadQOSClass = overrideClass; }
 #endif
@@ -109,6 +114,10 @@ private:
     void scavengeSmallPages(std::unique_lock<StaticMutex>&, ScavengeMode);
     void scavengeLargeObjects(std::unique_lock<StaticMutex>&, ScavengeMode);
 
+#if BPLATFORM(IOS)
+    void updateMemoryInUseParameters();
+#endif
+
     size_t m_vmPageSizePhysical;
     Vector<LineMetadata> m_smallLineMetadata;
     std::array<size_t, sizeClassCount> m_pageClasses;
@@ -129,11 +138,19 @@ private:
     Environment m_environment;
     DebugHeap* m_debugHeap;
 
+    std::chrono::milliseconds m_scavengeSleepDuration = { maxScavengeSleepDuration };
+
+#if BPLATFORM(IOS)
+    size_t m_maxAvailableMemory;
+    size_t m_memoryFootprint;
+    double m_percentAvailableMemoryInUse;
+#endif
+
     VMHeap m_vmHeap;
 
 #if BOS(DARWIN)
     dispatch_source_t m_pressureHandlerDispatchSource;
-    qos_class_t m_requestedScavengerThreadQOSClass { QOS_CLASS_UNSPECIFIED };
+    qos_class_t m_requestedScavengerThreadQOSClass { QOS_CLASS_USER_INITIATED };
 #endif
 };
 
@@ -153,6 +170,18 @@ inline void Heap::derefSmallLine(std::lock_guard<StaticMutex>& lock, Object obje
     deallocateSmallLine(lock, object);
 }
 
+#if BPLATFORM(IOS)
+inline size_t Heap::memoryFootprint()
+{
+    return m_memoryFootprint;
+}
+
+inline double Heap::percentAvailableMemoryInUse()
+{
+    return m_percentAvailableMemoryInUse;
+}
+#endif
+
 } // namespace bmalloc
 
 #endif // Heap_h
index 6b1f394..1666969 100644 (file)
@@ -68,7 +68,7 @@ namespace Sizes {
     static const size_t deallocatorLogCapacity = 256;
     static const size_t bumpRangeCacheCapacity = 3;
     
-    static const std::chrono::milliseconds scavengeSleepDuration = std::chrono::milliseconds(512);
+    static const std::chrono::milliseconds maxScavengeSleepDuration = std::chrono::milliseconds(250);
 
     static const size_t maskSizeClassCount = maskSizeClassMax / alignment;
 
index 6d1f1e1..59c12db 100644 (file)
@@ -23,6 +23,7 @@
  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 
  */
 
+#include "AvailableMemory.h"
 #include "Cache.h"
 #include "Heap.h"
 #include "PerProcess.h"
@@ -84,6 +85,23 @@ inline bool isEnabled()
     std::unique_lock<StaticMutex> lock(PerProcess<Heap>::mutex());
     return !PerProcess<Heap>::getFastCase()->debugHeap();
 }
+    
+inline size_t availableMemory()
+{
+    return bmalloc::availableMemory();
+}
+    
+#if BPLATFORM(IOS)
+inline size_t memoryFootprint()
+{
+    return PerProcess<Heap>::get()->memoryFootprint();
+}
+
+inline double percentAvailableMemoryInUse()
+{
+    return PerProcess<Heap>::get()->percentAvailableMemoryInUse();
+}
+#endif
 
 #if BOS(DARWIN)
 inline void setScavengerThreadQOSClass(qos_class_t overrideClass)