[pypy-commit] pypy default: hg merge use-madv-free
arigo
pypy.commits at gmail.com
Thu Jul 14 06:07:26 EDT 2016
Author: Armin Rigo <arigo at 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):
More information about the pypy-commit
mailing list