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