diff mercurial/util.py @ 51800:b619ba39d10a

mmap: populate mapping in a background thread When possible, we populate the memory mapping in a second thread. The mmap population does not only read the data from disk to memory. It also actually fill the memory mapping between process memory address and the physical memory used by the file system cache containing the mmap'ed data. Doing so buy back the slowdown from pre-population when it matters. When most data is accessed, only a few page fault will occurs, while the background thread fill the memory controller. When few data is accessed, the non-blocking mmap won't have to wait for all data to be populated. Here is a few example of improvement seen in benchmark around unbundle and push: ### data-env-vars.name = netbeans-2018-08-01-zstd-sparse-revlog # benchmark.name = hg.command.unbundle # benchmark.variants.issue6528 = disabled # benchmark.variants.reuse-external-delta-parent = yes # benchmark.variants.revs = any-100-extra-rev before: 0.758101 after: 0.732129 (-3.43%, -0.03) ## data-env-vars.name = mozilla-try-2019-02-18-zstd-sparse-revlog before: 1.519941 after: 1.503473 (-1.08%, -0.02) ### data-env-vars.name = mozilla-try-2019-02-18-zstd-sparse-revlog # benchmark.name = hg.command.push # bin-env-vars.hg.flavor = default # benchmark.variants.issue6528 = disabled # benchmark.variants.protocol = ssh # benchmark.variants.reuse-external-delta-parent = yes # benchmark.variants.revs = any-1-extra-rev before: 4.801442 after: 4.695810 (-1.46%, -0.07) # benchmark.variants.revs = any-100-extra-rev before: 4.848596 after: 4.794075 (-1.12%, -0.05) # bin-env-vars.hg.flavor = rust # benchmark.variants.revs = any-1-extra-rev before: 4.818410 after: 4.700053 (-2.46%, -0.12)
author Pierre-Yves David <pierre-yves.david@octobus.net>
date Tue, 09 Jul 2024 20:08:48 +0200
parents 92845af308b4
children 62806be5cbda
line wrap: on
line diff
--- a/mercurial/util.py	Thu Jul 25 14:40:38 2024 -0400
+++ b/mercurial/util.py	Tue Jul 09 20:08:48 2024 +0200
@@ -451,7 +451,9 @@
 
 
 def has_mmap_populate():
-    return hasattr(mmap, 'MAP_POPULATE')
+    return hasattr(osutil, "background_mmap_populate") or hasattr(
+        mmap, 'MAP_POPULATE'
+    )
 
 
 def mmapread(fp, size=None, pre_populate=True):
@@ -475,10 +477,13 @@
         size = 0
     fd = getattr(fp, 'fileno', lambda: fp)()
     flags = mmap.MAP_PRIVATE
-    if pre_populate:
+    bg_populate = hasattr(osutil, "background_mmap_populate")
+    if pre_populate and not bg_populate:
         flags |= getattr(mmap, 'MAP_POPULATE', 0)
     try:
         m = mmap.mmap(fd, size, flags=flags, prot=mmap.PROT_READ)
+        if pre_populate and bg_populate:
+            osutil.background_mmap_populate(m)
         return m
     except ValueError:
         # Empty files cannot be mmapped, but mmapread should still work.  Check