Author: Armin Rigo <ar...@tunes.org>
Branch: 
Changeset: r85689:95f85a57f5c9
Date: 2016-07-14 12:04 +0200
http://bitbucket.org/pypy/pypy/changeset/95f85a57f5c9/

Log:    hg merge use-madv-free

        Use madvise(MADV_FREE), or if that doesn't exist MADV_DONTNEED, on
        freed arenas. Fixes issue #2336.

diff --git a/rpython/memory/gc/minimarkpage.py 
b/rpython/memory/gc/minimarkpage.py
--- a/rpython/memory/gc/minimarkpage.py
+++ b/rpython/memory/gc/minimarkpage.py
@@ -395,6 +395,7 @@
                 if arena.nfreepages == arena.totalpages:
                     #
                     # The whole arena is empty.  Free it.
+                    llarena.arena_reset(arena.base, self.arena_size, 4)
                     llarena.arena_free(arena.base)
                     lltype.free(arena, flavor='raw', track_allocation=False)
                     #
diff --git a/rpython/rlib/rmmap.py b/rpython/rlib/rmmap.py
--- a/rpython/rlib/rmmap.py
+++ b/rpython/rlib/rmmap.py
@@ -70,7 +70,13 @@
     CConfig.MREMAP_MAYMOVE = (
         rffi_platform.DefinedConstantInteger("MREMAP_MAYMOVE"))
     CConfig.has_mremap = rffi_platform.Has('mremap(NULL, 0, 0, 0)')
-    # a dirty hack, this is probably a macro
+    CConfig.has_madvise = rffi_platform.Has('madvise(NULL, 0, 0)')
+    # ^^ both are a dirty hack, this is probably a macro
+
+    CConfig.MADV_DONTNEED = (
+        rffi_platform.DefinedConstantInteger('MADV_DONTNEED'))
+    CConfig.MADV_FREE = (
+        rffi_platform.DefinedConstantInteger('MADV_FREE'))
 
 elif _MS_WINDOWS:
     constant_names = ['PAGE_READONLY', 'PAGE_READWRITE', 'PAGE_WRITECOPY',
@@ -144,6 +150,7 @@
 
 if _POSIX:
     has_mremap = cConfig['has_mremap']
+    has_madvise = cConfig['has_madvise']
     c_mmap, c_mmap_safe = external('mmap', [PTR, size_t, rffi.INT, rffi.INT,
                                    rffi.INT, off_t], PTR, macro=True,
                                    save_err_on_unsafe=rffi.RFFI_SAVE_ERRNO)
@@ -154,6 +161,9 @@
     if has_mremap:
         c_mremap, _ = external('mremap',
                                [PTR, size_t, size_t, rffi.ULONG], PTR)
+    if has_madvise:
+        _, c_madvise_safe = external('madvise', [PTR, size_t, rffi.INT],
+                                     rffi.INT, _nowrapper=True)
 
     # this one is always safe
     _pagesize = rffi_platform.getintegerfunctionresult('getpagesize',
@@ -755,6 +765,39 @@
     else:
         free = c_munmap_safe
 
+    if sys.platform.startswith('linux'):
+        assert has_madvise
+        assert MADV_DONTNEED is not None
+        if MADV_FREE is None:
+            MADV_FREE = 8     # from the kernel sources of Linux >= 4.5
+        class CanUseMadvFree:
+            ok = -1
+        can_use_madv_free = CanUseMadvFree()
+        def madvise_free(addr, map_size):
+            # We don't know if we are running on a recent enough kernel
+            # that supports MADV_FREE.  Check that at runtime: if the
+            # first call to madvise(MADV_FREE) fails, we assume it's
+            # because of EINVAL and we fall back to MADV_DONTNEED.
+            if can_use_madv_free.ok != 0:
+                res = c_madvise_safe(rffi.cast(PTR, addr),
+                                     rffi.cast(size_t, map_size),
+                                     rffi.cast(rffi.INT, MADV_FREE))
+                if can_use_madv_free.ok == -1:
+                    can_use_madv_free.ok = (rffi.cast(lltype.Signed, res) == 0)
+            if can_use_madv_free.ok == 0:
+                c_madvise_safe(rffi.cast(PTR, addr),
+                               rffi.cast(size_t, map_size),
+                               rffi.cast(rffi.INT, MADV_DONTNEED))
+    elif has_madvise and not (MADV_FREE is MADV_DONTNEED is None):
+        use_flag = MADV_FREE if MADV_FREE is not None else MADV_DONTNEED
+        def madvise_free(addr, map_size):
+            c_madvise_safe(rffi.cast(PTR, addr),
+                           rffi.cast(size_t, map_size),
+                           rffi.cast(rffi.INT, use_flag))
+    else:
+        def madvice_free(addr, map_size):
+            "No madvice() on this platform"
+
 elif _MS_WINDOWS:
     def mmap(fileno, length, tagname="", access=_ACCESS_DEFAULT, offset=0):
         # XXX flags is or-ed into access by now.
@@ -906,5 +949,3 @@
 
     def free(ptr, map_size):
         VirtualFree_safe(ptr, 0, MEM_RELEASE)
-
-# register_external here?
diff --git a/rpython/rlib/test/test_rmmap.py b/rpython/rlib/test/test_rmmap.py
--- a/rpython/rlib/test/test_rmmap.py
+++ b/rpython/rlib/test/test_rmmap.py
@@ -5,6 +5,12 @@
 from rpython.rlib.rarithmetic import intmask
 from rpython.rlib import rmmap as mmap
 from rpython.rlib.rmmap import RTypeError, RValueError, alloc, free
+try:
+    from rpython.rlib.rmmap import madvise_free
+except ImportError:
+    def madvise_free(addr, size):
+        "Not available"
+
 
 class TestMMap:
     def setup_class(cls):
@@ -490,6 +496,7 @@
         data[i] = chr(i & 0xff)
     for i in range(0, map_size, 171):
         assert data[i] == chr(i & 0xff)
+    madvise_free(data, map_size)
     free(data, map_size)
 
 def test_compile_alloc_free():
diff --git a/rpython/rtyper/lltypesystem/llarena.py 
b/rpython/rtyper/lltypesystem/llarena.py
--- a/rpython/rtyper/lltypesystem/llarena.py
+++ b/rpython/rtyper/lltypesystem/llarena.py
@@ -52,7 +52,7 @@
                 del self.objectptrs[offset]
                 del self.objectsizes[offset]
                 obj._free()
-        if zero and zero != 3:
+        if zero in (1, 2):
             initialbyte = "0"
         else:
             initialbyte = "#"
@@ -335,6 +335,8 @@
       * 1: clear, optimized for a very large area of memory
       * 2: clear, optimized for a small or medium area of memory
       * 3: fill with garbage
+      * 4: large area of memory that can benefit from MADV_FREE
+             (i.e. contains garbage, may be zero-filled or not)
     """
     arena_addr = getfakearenaaddress(arena_addr)
     arena_addr.arena.reset(zero, arena_addr.offset, size)
@@ -410,16 +412,19 @@
             self.pagesize = 0
         def _cleanup_(self):
             self.pagesize = 0
+        def get(self):
+            pagesize = self.pagesize
+            if pagesize == 0:
+                pagesize = rffi.cast(lltype.Signed, legacy_getpagesize())
+                self.pagesize = pagesize
+            return pagesize
+
     posixpagesize = PosixPageSize()
 
     def clear_large_memory_chunk(baseaddr, size):
         from rpython.rlib import rmmap
 
-        pagesize = posixpagesize.pagesize
-        if pagesize == 0:
-            pagesize = rffi.cast(lltype.Signed, legacy_getpagesize())
-            posixpagesize.pagesize = pagesize
-
+        pagesize = posixpagesize.get()
         if size > 2 * pagesize:
             lowbits = rffi.cast(lltype.Signed, baseaddr) & (pagesize - 1)
             if lowbits:     # clear the initial misaligned part, if any
@@ -435,6 +440,17 @@
         if size > 0:    # clear the final misaligned part, if any
             llmemory.raw_memclear(baseaddr, size)
 
+    def madvise_arena_free(baseaddr, size):
+        from rpython.rlib import rmmap
+
+        pagesize = posixpagesize.get()
+        baseaddr = rffi.cast(lltype.Signed, baseaddr)
+        aligned_addr = (baseaddr + pagesize - 1) & ~(pagesize - 1)
+        size -= (aligned_addr - baseaddr)
+        if size >= pagesize:
+            rmmap.madvise_free(rffi.cast(rmmap.PTR, aligned_addr),
+                               size & ~(pagesize - 1))
+
 else:
     # XXX any better implementation on Windows?
     # Should use VirtualAlloc() to reserve the range of pages,
@@ -443,6 +459,12 @@
     # them immediately.
     clear_large_memory_chunk = llmemory.raw_memclear
 
+    def madvise_arena_free(baseaddr, size):
+        """XXX find a Windows equivalent?
+        'baseaddr' is in the middle of memory obtained with the C malloc()...
+        """
+
+
 if os.name == "posix":
     from rpython.translator.tool.cbuild import ExternalCompilationInfo
     _eci = ExternalCompilationInfo(includes=['sys/mman.h'])
@@ -509,6 +531,8 @@
             clear_large_memory_chunk(arena_addr, size)
         elif zero == 3:
             llop.raw_memset(lltype.Void, arena_addr, ord('#'), size)
+        elif zero == 4:
+            madvise_arena_free(arena_addr, size)
         else:
             llmemory.raw_memclear(arena_addr, size)
 llimpl_arena_reset._always_inline_ = True
diff --git a/rpython/rtyper/lltypesystem/test/test_llarena.py 
b/rpython/rtyper/lltypesystem/test/test_llarena.py
--- a/rpython/rtyper/lltypesystem/test/test_llarena.py
+++ b/rpython/rtyper/lltypesystem/test/test_llarena.py
@@ -1,6 +1,6 @@
-import py
+import py, os
 
-from rpython.rtyper.lltypesystem import lltype, llmemory
+from rpython.rtyper.lltypesystem import lltype, llmemory, rffi, llarena
 from rpython.rtyper.lltypesystem.llarena import (arena_malloc, arena_reset,
     arena_reserve, arena_free, round_up_for_allocation, ArenaError,
     arena_new_view, arena_shrink_obj, arena_protect, has_protect)
@@ -299,6 +299,29 @@
     p.x = 125
     assert p.x == 125
 
+def test_madvise_arena_free():
+    from rpython.rlib import rmmap
+
+    if os.name != 'posix':
+        py.test.skip("posix only")
+    pagesize = llarena.posixpagesize.get()
+    prev = rmmap.madvise_free
+    try:
+        seen = []
+        def my_madvise_free(addr, size):
+            assert lltype.typeOf(addr) == rmmap.PTR
+            seen.append((addr, size))
+        rmmap.madvise_free = my_madvise_free
+        llarena.madvise_arena_free(
+            rffi.cast(llmemory.Address, 123 * pagesize + 1),
+            pagesize * 7 - 2)
+    finally:
+        rmmap.madvise_free = prev
+    assert len(seen) == 1
+    addr, size = seen[0]
+    assert rffi.cast(lltype.Signed, addr) == 124 * pagesize
+    assert size == pagesize * 5
+
 
 class TestStandalone(test_standalone.StandaloneTests):
     def test_compiled_arena_protect(self):
_______________________________________________
pypy-commit mailing list
pypy-commit@python.org
https://mail.python.org/mailman/listinfo/pypy-commit

Reply via email to