Author: fijal
Branch:
Changeset: r93768:f23eec5d0d6d
Date: 2018-02-06 10:49 +0100
http://bitbucket.org/pypy/pypy/changeset/f23eec5d0d6d/
Log: Merge memory-accounting which adds extra functions to gc that let
you describe the whole memory
diff --git a/lib_pypy/_sqlite3.py b/lib_pypy/_sqlite3.py
--- a/lib_pypy/_sqlite3.py
+++ b/lib_pypy/_sqlite3.py
@@ -35,7 +35,7 @@
except ImportError:
assert '__pypy__' not in sys.builtin_module_names
newlist_hint = lambda sizehint: []
- add_memory_pressure = lambda size: None
+ add_memory_pressure = lambda size, obj: None
if sys.version_info[0] >= 3:
StandardError = Exception
@@ -153,9 +153,10 @@
factory = Connection if not factory else factory
# an sqlite3 db seems to be around 100 KiB at least (doesn't matter if
# backed by :memory: or a file)
- add_memory_pressure(100 * 1024)
- return factory(database, timeout, detect_types, isolation_level,
+ res = factory(database, timeout, detect_types, isolation_level,
check_same_thread, factory, cached_statements)
+ add_memory_pressure(100 * 1024, res)
+ return res
def _unicode_text_factory(x):
diff --git a/pypy/module/__pypy__/interp_magic.py
b/pypy/module/__pypy__/interp_magic.py
--- a/pypy/module/__pypy__/interp_magic.py
+++ b/pypy/module/__pypy__/interp_magic.py
@@ -142,11 +142,14 @@
space.newbool(debug))
@unwrap_spec(estimate=int)
-def add_memory_pressure(estimate):
+def add_memory_pressure(space, estimate, w_obj=None):
""" Add memory pressure of estimate bytes. Useful when calling a C function
that internally allocates a big chunk of memory. This instructs the GC to
garbage collect sooner than it would otherwise."""
+ #if space.is_none(w_obj):
rgc.add_memory_pressure(estimate)
+ #else:
+ # rgc.add_memory_pressure(estimate, w_obj)
@unwrap_spec(w_frame=PyFrame)
def locals_to_fast(space, w_frame):
diff --git a/pypy/module/_cffi_backend/allocator.py
b/pypy/module/_cffi_backend/allocator.py
--- a/pypy/module/_cffi_backend/allocator.py
+++ b/pypy/module/_cffi_backend/allocator.py
@@ -21,13 +21,13 @@
if self.w_alloc is None:
if self.should_clear_after_alloc:
ptr = lltype.malloc(rffi.CCHARP.TO, datasize,
- flavor='raw', zero=True,
- add_memory_pressure=True)
+ flavor='raw', zero=True)
else:
ptr = lltype.malloc(rffi.CCHARP.TO, datasize,
- flavor='raw', zero=False,
- add_memory_pressure=True)
- return cdataobj.W_CDataNewStd(space, ptr, ctype, length)
+ flavor='raw', zero=False)
+ w_res = cdataobj.W_CDataNewStd(space, ptr, ctype, length)
+ rgc.add_memory_pressure(datasize, w_res)
+ return w_res
else:
w_raw_cdata = space.call_function(self.w_alloc,
space.newint(datasize))
@@ -53,7 +53,7 @@
if self.w_free is not None:
res.w_free = self.w_free
res.register_finalizer(space)
- rgc.add_memory_pressure(datasize)
+ rgc.add_memory_pressure(datasize, res)
return res
@unwrap_spec(w_init=WrappedDefault(None))
diff --git a/pypy/module/_cffi_backend/cdataobj.py
b/pypy/module/_cffi_backend/cdataobj.py
--- a/pypy/module/_cffi_backend/cdataobj.py
+++ b/pypy/module/_cffi_backend/cdataobj.py
@@ -447,7 +447,10 @@
with self as ptr:
w_res = W_CDataGCP(space, ptr, self.ctype, self, w_destructor)
if size != 0:
- rgc.add_memory_pressure(size)
+ if isinstance(w_res, W_CDataGCP):
+ rgc.add_memory_pressure(size, w_res)
+ else:
+ rgc.add_memory_pressure(size, self)
return w_res
def unpack(self, length):
diff --git a/pypy/module/_hashlib/interp_hashlib.py
b/pypy/module/_hashlib/interp_hashlib.py
--- a/pypy/module/_hashlib/interp_hashlib.py
+++ b/pypy/module/_hashlib/interp_hashlib.py
@@ -61,7 +61,8 @@
ctx = ropenssl.EVP_MD_CTX_new()
if ctx is None:
raise MemoryError
- rgc.add_memory_pressure(ropenssl.HASH_MALLOC_SIZE + self.digest_size)
+ rgc.add_memory_pressure(ropenssl.HASH_MALLOC_SIZE + self.digest_size,
+ self)
try:
if copy_from:
if not ropenssl.EVP_MD_CTX_copy(ctx, copy_from):
diff --git a/pypy/module/_ssl/interp_ssl.py b/pypy/module/_ssl/interp_ssl.py
--- a/pypy/module/_ssl/interp_ssl.py
+++ b/pypy/module/_ssl/interp_ssl.py
@@ -1316,8 +1316,9 @@
if not ctx:
raise ssl_error(space, "failed to allocate SSL context")
- rgc.add_memory_pressure(10 * 1024 * 1024)
self = space.allocate_instance(_SSLContext, w_subtype)
+ assert isinstance(self, _SSLContext)
+ rgc.add_memory_pressure(10 * 1024 * 1024, self)
self.ctx = ctx
self.check_hostname = False
self.register_finalizer(space)
diff --git a/pypy/module/gc/__init__.py b/pypy/module/gc/__init__.py
--- a/pypy/module/gc/__init__.py
+++ b/pypy/module/gc/__init__.py
@@ -19,6 +19,7 @@
space.config.translation.gctransformer == "framework"):
self.appleveldefs.update({
'dump_rpy_heap': 'app_referents.dump_rpy_heap',
+ 'get_stats': 'app_referents.get_stats',
})
self.interpleveldefs.update({
'get_rpy_roots': 'referents.get_rpy_roots',
@@ -28,6 +29,7 @@
'get_objects': 'referents.get_objects',
'get_referents': 'referents.get_referents',
'get_referrers': 'referents.get_referrers',
+ '_get_stats': 'referents.get_stats',
'_dump_rpy_heap': 'referents._dump_rpy_heap',
'get_typeids_z': 'referents.get_typeids_z',
'get_typeids_list': 'referents.get_typeids_list',
diff --git a/pypy/module/gc/app_referents.py b/pypy/module/gc/app_referents.py
--- a/pypy/module/gc/app_referents.py
+++ b/pypy/module/gc/app_referents.py
@@ -48,3 +48,49 @@
file.flush()
fd = file.fileno()
gc._dump_rpy_heap(fd)
+
+class GcStats(object):
+ def __init__(self, s):
+ self._s = s
+ for item in ('total_gc_memory', 'jit_backend_used',
+ 'total_memory_pressure',
+ 'total_allocated_memory', 'jit_backend_allocated',
+ 'peak_memory', 'peak_allocated_memory'):
+ setattr(self, item, self._format(getattr(self._s, item)))
+ self.memory_used_sum = self._format(self._s.total_gc_memory +
self._s.total_memory_pressure +
+ self._s.jit_backend_used)
+ self.memory_allocated_sum =
self._format(self._s.total_allocated_memory + self._s.total_memory_pressure +
+ self._s.jit_backend_allocated)
+
+ def _format(self, v):
+ if v < 1000000:
+ # bit unlikely ;-)
+ return "%.1fkB" % (v / 1024.)
+ return "%.1fMB" % (v / 1024. / 1024.)
+
+ def repr(self):
+ return """Total memory consumed:
+GC used: %s (peak: %s)
+raw assembler used: %s
+memory pressure: %s
+-----------------------------
+Total: %s
+
+Total memory allocated:
+GC allocated: %s (peak: %s)
+raw assembler allocated: %s
+memory pressure: %s
+-----------------------------
+Total: %s
+""" % (self.total_gc_memory, self.peak_memory,
+ self.jit_backend_used,
+ self.total_memory_pressure,
+ self.memory_used_sum,
+
+ self.total_allocated_memory, self.peak_allocated_memory,
+ self.jit_backend_allocated,
+ self.total_memory_pressure,
+ self.memory_allocated_sum)
+
+def get_stats():
+ return GcStats(gc._get_stats())
diff --git a/pypy/module/gc/referents.py b/pypy/module/gc/referents.py
--- a/pypy/module/gc/referents.py
+++ b/pypy/module/gc/referents.py
@@ -1,7 +1,7 @@
-from rpython.rlib import rgc
+from rpython.rlib import rgc, jit_hooks
from pypy.interpreter.baseobjspace import W_Root
-from pypy.interpreter.typedef import TypeDef
-from pypy.interpreter.gateway import unwrap_spec
+from pypy.interpreter.typedef import TypeDef, interp_attrproperty
+from pypy.interpreter.gateway import unwrap_spec, interp2app
from pypy.interpreter.error import oefmt, wrap_oserror
from rpython.rlib.objectmodel import we_are_translated
@@ -170,3 +170,33 @@
l = rgc.get_typeids_list()
list_w = [space.newint(l[i]) for i in range(len(l))]
return space.newlist(list_w)
+
+class W_GcStats(W_Root):
+ def __init__(self):
+ self.total_memory_pressure = rgc.get_stats(rgc.TOTAL_MEMORY_PRESSURE)
+ self.total_gc_memory = rgc.get_stats(rgc.TOTAL_MEMORY)
+ self.total_allocated_memory = rgc.get_stats(rgc.TOTAL_ALLOCATED_MEMORY)
+ self.peak_memory = rgc.get_stats(rgc.PEAK_MEMORY)
+ self.peak_allocated_memory = rgc.get_stats(rgc.PEAK_ALLOCATED_MEMORY)
+ self.jit_backend_allocated = jit_hooks.stats_asmmemmgr_allocated(None)
+ self.jit_backend_used = jit_hooks.stats_asmmemmgr_used(None)
+
+W_GcStats.typedef = TypeDef("GcStats",
+ total_memory_pressure=interp_attrproperty("total_memory_pressure",
+ cls=W_GcStats, wrapfn="newint"),
+ total_gc_memory=interp_attrproperty("total_gc_memory",
+ cls=W_GcStats, wrapfn="newint"),
+ peak_allocated_memory=interp_attrproperty("peak_allocated_memory",
+ cls=W_GcStats, wrapfn="newint"),
+ peak_memory=interp_attrproperty("peak_memory",
+ cls=W_GcStats, wrapfn="newint"),
+ total_allocated_memory=interp_attrproperty("total_allocated_memory",
+ cls=W_GcStats, wrapfn="newint"),
+ jit_backend_allocated=interp_attrproperty("jit_backend_allocated",
+ cls=W_GcStats, wrapfn="newint"),
+ jit_backend_used=interp_attrproperty("jit_backend_used",
+ cls=W_GcStats, wrapfn="newint"),
+)
+
+def get_stats(space):
+ return W_GcStats()
diff --git a/pypy/module/pyexpat/interp_pyexpat.py
b/pypy/module/pyexpat/interp_pyexpat.py
--- a/pypy/module/pyexpat/interp_pyexpat.py
+++ b/pypy/module/pyexpat/interp_pyexpat.py
@@ -840,11 +840,11 @@
# Currently this is just the size of the pointer and some estimated bytes.
# The struct isn't actually defined in expat.h - it is in xmlparse.c
# XXX: find a good estimate of the XML_ParserStruct
- rgc.add_memory_pressure(XML_Parser_SIZE + 300)
if not xmlparser:
raise oefmt(space.w_RuntimeError, "XML_ParserCreate failed")
parser = W_XMLParserType(space, xmlparser, w_intern)
+ rgc.add_memory_pressure(XML_Parser_SIZE + 300, parser)
XML_SetUnknownEncodingHandler(
parser.itself, UnknownEncodingHandlerData_callback,
rffi.cast(rffi.VOIDP, parser.id))
diff --git a/rpython/annotator/bookkeeper.py b/rpython/annotator/bookkeeper.py
--- a/rpython/annotator/bookkeeper.py
+++ b/rpython/annotator/bookkeeper.py
@@ -71,6 +71,7 @@
self.needs_generic_instantiate = {}
self.thread_local_fields = set()
+ self.memory_pressure_types = set()
self.register_builtins()
diff --git a/rpython/jit/codewriter/support.py
b/rpython/jit/codewriter/support.py
--- a/rpython/jit/codewriter/support.py
+++ b/rpython/jit/codewriter/support.py
@@ -675,6 +675,8 @@
def _ll_1_gc_add_memory_pressure(num):
llop.gc_add_memory_pressure(lltype.Void, num)
+ def _ll_2_gc_add_memory_pressure(num, obj):
+ llop.gc_add_memory_pressure(lltype.Void, num, obj)
def setup_extra_builtin(rtyper, oopspec_name, nb_args, extra=None):
diff --git a/rpython/memory/gc/base.py b/rpython/memory/gc/base.py
--- a/rpython/memory/gc/base.py
+++ b/rpython/memory/gc/base.py
@@ -83,7 +83,9 @@
has_custom_trace,
fast_path_tracing,
has_gcptr,
- cannot_pin):
+ cannot_pin,
+ has_memory_pressure,
+ get_memory_pressure_ofs):
self.finalizer_handlers = finalizer_handlers
self.destructor_or_custom_trace = destructor_or_custom_trace
self.is_old_style_finalizer = is_old_style_finalizer
@@ -103,6 +105,8 @@
self.fast_path_tracing = fast_path_tracing
self.has_gcptr = has_gcptr
self.cannot_pin = cannot_pin
+ self.has_memory_pressure = has_memory_pressure
+ self.get_memory_pressure_ofs = get_memory_pressure_ofs
def get_member_index(self, type_id):
return self.member_index(type_id)
diff --git a/rpython/memory/gc/incminimark.py b/rpython/memory/gc/incminimark.py
--- a/rpython/memory/gc/incminimark.py
+++ b/rpython/memory/gc/incminimark.py
@@ -72,6 +72,7 @@
from rpython.rlib.rarithmetic import LONG_BIT_SHIFT
from rpython.rlib.debug import ll_assert, debug_print, debug_start, debug_stop
from rpython.rlib.objectmodel import specialize
+from rpython.rlib import rgc
from rpython.memory.gc.minimarkpage import out_of_memory
#
@@ -371,6 +372,7 @@
self.old_rawmalloced_objects = self.AddressStack()
self.raw_malloc_might_sweep = self.AddressStack()
self.rawmalloced_total_size = r_uint(0)
+ self.rawmalloced_peak_size = r_uint(0)
self.gc_state = STATE_SCANNING
#
@@ -996,6 +998,8 @@
# Record the newly allocated object and its full malloced size.
# The object is young or old depending on the argument.
self.rawmalloced_total_size += r_uint(allocsize)
+ self.rawmalloced_peak_size = max(self.rawmalloced_total_size,
+ self.rawmalloced_peak_size)
if alloc_young:
if not self.young_rawmalloced_objects:
self.young_rawmalloced_objects = self.AddressDict()
@@ -1023,7 +1027,7 @@
if self.max_heap_size < self.next_major_collection_threshold:
self.next_major_collection_threshold = self.max_heap_size
- def raw_malloc_memory_pressure(self, sizehint):
+ def raw_malloc_memory_pressure(self, sizehint, adr):
# Decrement by 'sizehint' plus a very little bit extra. This
# is needed e.g. for _rawffi, which may allocate a lot of tiny
# arrays.
@@ -1183,6 +1187,24 @@
"""
return self.ac.total_memory_used + self.rawmalloced_total_size
+ def get_total_memory_alloced(self):
+ """ Return the total memory allocated
+ """
+ return self.ac.total_memory_alloced + self.rawmalloced_total_size
+
+ def get_peak_memory_alloced(self):
+ """ Return the peak memory ever allocated. The peaks
+ can be at different times, but we just don't worry for now
+ """
+ return self.ac.peak_memory_alloced + self.rawmalloced_peak_size
+
+ def get_peak_memory_used(self):
+ """ Return the peak memory GC felt ever responsible for
+ """
+ mem_allocated = max(self.ac.peak_memory_used,
+ self.ac.total_memory_used)
+ return mem_allocated + self.rawmalloced_peak_size
+
def threshold_reached(self, extra=0):
return (self.next_major_collection_threshold -
float(self.get_total_memory_used())) < float(extra)
@@ -2155,6 +2177,8 @@
#
size_gc_header = self.gcheaderbuilder.size_gc_header
self.rawmalloced_total_size += r_uint(raw_malloc_usage(totalsize))
+ self.rawmalloced_peak_size = max(self.rawmalloced_total_size,
+ self.rawmalloced_peak_size)
self.old_rawmalloced_objects.append(arena + size_gc_header)
return arena
@@ -2932,6 +2956,21 @@
self.old_objects_with_weakrefs.delete()
self.old_objects_with_weakrefs = new_with_weakref
+ def get_stats(self, stats_no):
+ from rpython.memory.gc import inspector
+
+ if stats_no == rgc.TOTAL_MEMORY:
+ return intmask(self.get_total_memory_used() + self.nursery_size)
+ elif stats_no == rgc.PEAK_MEMORY:
+ return intmask(self.get_peak_memory_used() + self.nursery_size)
+ elif stats_no == rgc.PEAK_ALLOCATED_MEMORY:
+ return intmask(self.get_peak_memory_alloced() + self.nursery_size)
+ elif stats_no == rgc.TOTAL_ALLOCATED_MEMORY:
+ return intmask(self.get_total_memory_alloced() + self.nursery_size)
+ elif stats_no == rgc.TOTAL_MEMORY_PRESSURE:
+ return inspector.count_memory_pressure(self)
+ return 0
+
# ----------
# RawRefCount
diff --git a/rpython/memory/gc/inspector.py b/rpython/memory/gc/inspector.py
--- a/rpython/memory/gc/inspector.py
+++ b/rpython/memory/gc/inspector.py
@@ -2,6 +2,7 @@
Utility RPython functions to inspect objects in the GC.
"""
from rpython.rtyper.lltypesystem import lltype, llmemory, rffi, llgroup
+from rpython.rtyper.lltypesystem.lloperation import llop
from rpython.rlib.objectmodel import free_non_gc_object
from rpython.rlib import rposix, rgc, jit
@@ -92,17 +93,12 @@
AddressStack = get_address_stack()
-class HeapDumper(object):
- _alloc_flavor_ = "raw"
- BUFSIZE = 8192 # words
+class BaseWalker(object):
+ _alloc_flavor_ = 'raw'
- def __init__(self, gc, fd):
+ def __init__(self, gc):
self.gc = gc
self.gcflag = gc.gcflag_extra
- self.fd = rffi.cast(rffi.INT, fd)
- self.writebuffer = lltype.malloc(rffi.SIGNEDP.TO, self.BUFSIZE,
- flavor='raw')
- self.buf_count = 0
if self.gcflag == 0:
self.seen = AddressDict()
self.pending = AddressStack()
@@ -111,8 +107,107 @@
if self.gcflag == 0:
self.seen.delete()
self.pending.delete()
+ free_non_gc_object(self)
+
+ def add_roots(self):
+ self.gc.enumerate_all_roots(_hd_add_root, self)
+ pendingroots = self.pending
+ self.pending = AddressStack()
+ self.walk(pendingroots)
+ pendingroots.delete()
+ self.end_add_roots_marker()
+
+ def end_add_roots_marker(self):
+ pass
+
+ def add(self, obj):
+ if self.gcflag == 0:
+ if not self.seen.contains(obj):
+ self.seen.setitem(obj, obj)
+ self.pending.append(obj)
+ else:
+ hdr = self.gc.header(obj)
+ if (hdr.tid & self.gcflag) == 0:
+ hdr.tid |= self.gcflag
+ self.pending.append(obj)
+
+ def walk(self, pending):
+ while pending.non_empty():
+ self.processobj(pending.pop())
+
+ # ----------
+ # A simplified copy of the above, to make sure we walk again all the
+ # objects to clear the 'gcflag'.
+
+ def unobj(self, obj):
+ gc = self.gc
+ gc.trace(obj, self._unref, None)
+
+ def _unref(self, pointer, _):
+ obj = pointer.address[0]
+ self.unadd(obj)
+
+ def unadd(self, obj):
+ assert self.gcflag != 0
+ hdr = self.gc.header(obj)
+ if (hdr.tid & self.gcflag) != 0:
+ hdr.tid &= ~self.gcflag
+ self.pending.append(obj)
+
+ def clear_gcflag_again(self):
+ self.gc.enumerate_all_roots(_hd_unadd_root, self)
+ pendingroots = self.pending
+ self.pending = AddressStack()
+ self.unwalk(pendingroots)
+ pendingroots.delete()
+
+ def unwalk(self, pending):
+ while pending.non_empty():
+ self.unobj(pending.pop())
+
+ def finish_processing(self):
+ if self.gcflag != 0:
+ self.clear_gcflag_again()
+ self.unwalk(self.pending)
+
+ def process(self):
+ self.add_roots()
+ self.walk(self.pending)
+
+
+class MemoryPressureCounter(BaseWalker):
+
+ def __init__(self, gc):
+ self.count = 0
+ BaseWalker.__init__(self, gc)
+
+ def processobj(self, obj):
+ gc = self.gc
+ typeid = gc.get_type_id(obj)
+ if gc.has_memory_pressure(typeid):
+ ofs = gc.get_memory_pressure_ofs(typeid)
+ val = (obj + ofs).signed[0]
+ self.count += val
+ gc.trace(obj, self._ref, None)
+
+ def _ref(self, pointer, _):
+ obj = pointer.address[0]
+ self.add(obj)
+
+
+class HeapDumper(BaseWalker):
+ BUFSIZE = 8192 # words
+
+ def __init__(self, gc, fd):
+ BaseWalker.__init__(self, gc)
+ self.fd = rffi.cast(rffi.INT, fd)
+ self.writebuffer = lltype.malloc(rffi.SIGNEDP.TO, self.BUFSIZE,
+ flavor='raw')
+ self.buf_count = 0
+
+ def delete(self):
lltype.free(self.writebuffer, flavor='raw')
- free_non_gc_object(self)
+ BaseWalker.delete(self)
@jit.dont_look_inside
def flush(self):
@@ -143,6 +238,7 @@
self.write(0)
self.write(0)
self.write(-1)
+ end_add_roots_marker = write_marker
def writeobj(self, obj):
gc = self.gc
@@ -152,64 +248,13 @@
self.write(gc.get_size_incl_hash(obj))
gc.trace(obj, self._writeref, None)
self.write(-1)
+ processobj = writeobj
def _writeref(self, pointer, _):
obj = pointer.address[0]
self.write(llmemory.cast_adr_to_int(obj))
self.add(obj)
- def add(self, obj):
- if self.gcflag == 0:
- if not self.seen.contains(obj):
- self.seen.setitem(obj, obj)
- self.pending.append(obj)
- else:
- hdr = self.gc.header(obj)
- if (hdr.tid & self.gcflag) == 0:
- hdr.tid |= self.gcflag
- self.pending.append(obj)
-
- def add_roots(self):
- self.gc.enumerate_all_roots(_hd_add_root, self)
- pendingroots = self.pending
- self.pending = AddressStack()
- self.walk(pendingroots)
- pendingroots.delete()
- self.write_marker()
-
- def walk(self, pending):
- while pending.non_empty():
- self.writeobj(pending.pop())
-
- # ----------
- # A simplified copy of the above, to make sure we walk again all the
- # objects to clear the 'gcflag'.
-
- def unwriteobj(self, obj):
- gc = self.gc
- gc.trace(obj, self._unwriteref, None)
-
- def _unwriteref(self, pointer, _):
- obj = pointer.address[0]
- self.unadd(obj)
-
- def unadd(self, obj):
- assert self.gcflag != 0
- hdr = self.gc.header(obj)
- if (hdr.tid & self.gcflag) != 0:
- hdr.tid &= ~self.gcflag
- self.pending.append(obj)
-
- def clear_gcflag_again(self):
- self.gc.enumerate_all_roots(_hd_unadd_root, self)
- pendingroots = self.pending
- self.pending = AddressStack()
- self.unwalk(pendingroots)
- pendingroots.delete()
-
- def unwalk(self, pending):
- while pending.non_empty():
- self.unwriteobj(pending.pop())
def _hd_add_root(obj, heap_dumper):
heap_dumper.add(obj)
@@ -219,15 +264,20 @@
def dump_rpy_heap(gc, fd):
heapdumper = HeapDumper(gc, fd)
- heapdumper.add_roots()
- heapdumper.walk(heapdumper.pending)
+ heapdumper.process()
heapdumper.flush()
- if heapdumper.gcflag != 0:
- heapdumper.clear_gcflag_again()
- heapdumper.unwalk(heapdumper.pending)
+ heapdumper.finish_processing()
heapdumper.delete()
return True
+def count_memory_pressure(gc):
+ counter = MemoryPressureCounter(gc)
+ counter.process()
+ counter.finish_processing()
+ res = counter.count
+ counter.delete()
+ return res
+
def get_typeids_z(gc):
srcaddress = gc.root_walker.gcdata.typeids_z
return llmemory.cast_adr_to_ptr(srcaddress, lltype.Ptr(rgc.ARRAY_OF_CHAR))
diff --git a/rpython/memory/gc/minimark.py b/rpython/memory/gc/minimark.py
--- a/rpython/memory/gc/minimark.py
+++ b/rpython/memory/gc/minimark.py
@@ -828,7 +828,7 @@
if self.max_heap_size < self.next_major_collection_threshold:
self.next_major_collection_threshold = self.max_heap_size
- def raw_malloc_memory_pressure(self, sizehint):
+ def raw_malloc_memory_pressure(self, sizehint, adr):
self.next_major_collection_threshold -= sizehint
if self.next_major_collection_threshold < 0:
# cannot trigger a full collection now, but we can ensure
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
@@ -141,6 +141,9 @@
# the total memory used, counting every block in use, without
# the additional bookkeeping stuff.
self.total_memory_used = r_uint(0)
+ self.peak_memory_used = r_uint(0)
+ self.total_memory_alloced = r_uint(0)
+ self.peak_memory_alloced = r_uint(0)
def _new_page_ptr_list(self, length):
@@ -293,7 +296,11 @@
#
# 'arena_base' points to the start of malloced memory; it might not
# be a page-aligned address
- arena_base = llarena.arena_malloc(self.arena_size, False)
+ arena_base = llarena.arena_mmap(self.arena_size)
+ self.total_memory_alloced += self.arena_size
+ self.peak_memory_alloced = max(self.total_memory_alloced,
+ self.peak_memory_alloced)
+
if not arena_base:
out_of_memory("out of memory: couldn't allocate the next arena")
arena_end = arena_base + self.arena_size
@@ -321,6 +328,8 @@
"""Prepare calls to mass_free_incremental(): moves the chained lists
into 'self.old_xxx'.
"""
+ self.peak_memory_used = max(self.peak_memory_used,
+ self.total_memory_used)
self.total_memory_used = r_uint(0)
#
size_class = self.small_request_threshold >> WORD_POWER_2
@@ -397,8 +406,8 @@
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)
+ llarena.arena_munmap(arena.base, self.arena_size)
+ self.total_memory_alloced -= self.arena_size
lltype.free(arena, flavor='raw', track_allocation=False)
self.arenas_count -= 1
#
diff --git a/rpython/memory/gctransform/framework.py
b/rpython/memory/gctransform/framework.py
--- a/rpython/memory/gctransform/framework.py
+++ b/rpython/memory/gctransform/framework.py
@@ -392,23 +392,30 @@
inline = True)
if getattr(GCClass, 'raw_malloc_memory_pressure', False):
- def raw_malloc_memory_pressure_varsize(length, itemsize):
+ def raw_malloc_memory_pressure_varsize(length, itemsize, adr):
totalmem = length * itemsize
if totalmem > 0:
- gcdata.gc.raw_malloc_memory_pressure(totalmem)
+ gcdata.gc.raw_malloc_memory_pressure(totalmem, adr)
#else: probably an overflow -- the following rawmalloc
# will fail then
- def raw_malloc_memory_pressure(sizehint):
- gcdata.gc.raw_malloc_memory_pressure(sizehint)
+ def raw_malloc_memory_pressure(sizehint, adr):
+ gcdata.gc.raw_malloc_memory_pressure(sizehint, adr)
self.raw_malloc_memory_pressure_varsize_ptr = getfn(
raw_malloc_memory_pressure_varsize,
- [annmodel.SomeInteger(), annmodel.SomeInteger()],
+ [annmodel.SomeInteger(), annmodel.SomeInteger(),
+ SomeAddress()],
annmodel.s_None, minimal_transform = False)
self.raw_malloc_memory_pressure_ptr = getfn(
raw_malloc_memory_pressure,
- [annmodel.SomeInteger()],
+ [annmodel.SomeInteger(), SomeAddress()],
annmodel.s_None, minimal_transform = False)
+ if getattr(GCClass, 'get_stats', False):
+ def get_stats(stats_no):
+ return gcdata.gc.get_stats(stats_no)
+ self.get_stats_ptr = getfn(get_stats, [annmodel.SomeInteger()],
+ annmodel.SomeInteger())
+
self.identityhash_ptr = getfn(GCClass.identityhash.im_func,
[s_gc, s_gcref],
@@ -831,6 +838,39 @@
gct_fv_gc_malloc_varsize = gct_fv_gc_malloc
+ def gct_gc_add_memory_pressure(self, hop):
+ def _find_correct_type(TP):
+ T = TP.TO
+ while 'special_memory_pressure' not in T._flds:
+ T = T._flds['super']
+ return T
+
+ if hasattr(self, 'raw_malloc_memory_pressure_ptr'):
+ op = hop.spaceop
+ size = op.args[0]
+ if len(op.args) == 2:
+ v_fld = rmodel.inputconst(lltype.Void,
"special_memory_pressure")
+ T = _find_correct_type(op.args[1].concretetype)
+ v_inst = hop.genop("cast_pointer", [op.args[1]],
+ resulttype=lltype.Ptr(T))
+ hop.genop("bare_setfield", [v_inst, v_fld, size])
+ v_adr = hop.genop("cast_ptr_to_adr", [op.args[1]],
+ resulttype=llmemory.Address)
+ else:
+ v_adr = rmodel.inputconst(llmemory.Address, llmemory.NULL)
+ hop.genop("direct_call", [self.raw_malloc_memory_pressure_ptr,
+ size, v_adr])
+
+
+ def gct_gc_get_stats(self, hop):
+ if hasattr(self, 'get_stats_ptr'):
+ return hop.genop("direct_call",
+ [self.get_stats_ptr, hop.spaceop.args[0]],
+ resultvar=hop.spaceop.result)
+ hop.genop("same_as", [rmodel.inputconst(lltype.Signed, 0)],
+ resultvar=hop.spaceop.result)
+
+
def gct_gc__collect(self, hop):
op = hop.spaceop
if len(op.args) == 1:
diff --git a/rpython/memory/gctransform/transform.py
b/rpython/memory/gctransform/transform.py
--- a/rpython/memory/gctransform/transform.py
+++ b/rpython/memory/gctransform/transform.py
@@ -535,12 +535,7 @@
return self.varsize_malloc_helper(hop, flags, meth, [])
def gct_gc_add_memory_pressure(self, hop):
- if hasattr(self, 'raw_malloc_memory_pressure_ptr'):
- op = hop.spaceop
- size = op.args[0]
- return hop.genop("direct_call",
- [self.raw_malloc_memory_pressure_ptr,
- size])
+ pass
def varsize_malloc_helper(self, hop, flags, meth, extraargs):
def intconst(c): return rmodel.inputconst(lltype.Signed, c)
@@ -574,9 +569,10 @@
c_offset_to_length):
if flags.get('add_memory_pressure', False):
if hasattr(self, 'raw_malloc_memory_pressure_varsize_ptr'):
+ v_adr = rmodel.inputconst(llmemory.Address, llmemory.NULL)
hop.genop("direct_call",
[self.raw_malloc_memory_pressure_varsize_ptr,
- v_length, c_item_size])
+ v_length, c_item_size, v_adr])
if c_offset_to_length is None:
if flags.get('zero'):
fnptr = self.raw_malloc_varsize_no_length_zero_ptr
diff --git a/rpython/memory/gctypelayout.py b/rpython/memory/gctypelayout.py
--- a/rpython/memory/gctypelayout.py
+++ b/rpython/memory/gctypelayout.py
@@ -21,13 +21,21 @@
# A destructor is called when the object is about to be freed.
# A custom tracer (CT) enumerates the addresses that contain GCREFs.
# Both are called with the address of the object as only argument.
+ # They're embedded in a struct that has raw_memory_offset as another
+ # argument, which is only valid if T_HAS_MEMORY_PRESSURE is set
CUSTOM_FUNC = lltype.FuncType([llmemory.Address], lltype.Void)
CUSTOM_FUNC_PTR = lltype.Ptr(CUSTOM_FUNC)
+ CUSTOM_DATA_STRUCT = lltype.Struct('custom_data',
+ ('customfunc', CUSTOM_FUNC_PTR),
+ ('memory_pressure_offset', lltype.Signed), # offset to where the amount
+ # of owned memory pressure is stored
+ )
+ CUSTOM_DATA_STRUCT_PTR = lltype.Ptr(CUSTOM_DATA_STRUCT)
# structure describing the layout of a typeid
TYPE_INFO = lltype.Struct("type_info",
("infobits", lltype.Signed), # combination of the T_xxx consts
- ("customfunc", CUSTOM_FUNC_PTR),
+ ("customdata", CUSTOM_DATA_STRUCT_PTR),
("fixedsize", lltype.Signed),
("ofstoptrs", lltype.Ptr(OFFSETS_TO_GC_PTR)),
hints={'immutable': True},
@@ -81,14 +89,14 @@
def q_cannot_pin(self, typeid):
typeinfo = self.get(typeid)
ANY = (T_HAS_GCPTR | T_IS_WEAKREF)
- return (typeinfo.infobits & ANY) != 0 or bool(typeinfo.customfunc)
+ return (typeinfo.infobits & ANY) != 0 or bool(typeinfo.customdata)
def q_finalizer_handlers(self):
adr = self.finalizer_handlers # set from framework.py or gcwrapper.py
return llmemory.cast_adr_to_ptr(adr, lltype.Ptr(FIN_HANDLER_ARRAY))
def q_destructor_or_custom_trace(self, typeid):
- return self.get(typeid).customfunc
+ return self.get(typeid).customdata.customfunc
def q_is_old_style_finalizer(self, typeid):
typeinfo = self.get(typeid)
@@ -139,6 +147,15 @@
infobits = self.get(typeid).infobits
return infobits & T_ANY_SLOW_FLAG == 0
+ def q_has_memory_pressure(self, typeid):
+ infobits = self.get(typeid).infobits
+ return infobits & T_HAS_MEMORY_PRESSURE != 0
+
+ def q_get_memory_pressure_ofs(self, typeid):
+ infobits = self.get(typeid).infobits
+ assert infobits & T_HAS_MEMORY_PRESSURE != 0
+ return self.get(typeid).customdata.memory_pressure_offset
+
def set_query_functions(self, gc):
gc.set_query_functions(
self.q_is_varsize,
@@ -159,7 +176,9 @@
self.q_has_custom_trace,
self.q_fast_path_tracing,
self.q_has_gcptr,
- self.q_cannot_pin)
+ self.q_cannot_pin,
+ self.q_has_memory_pressure,
+ self.q_get_memory_pressure_ofs)
def _has_got_custom_trace(self, typeid):
type_info = self.get(typeid)
@@ -176,8 +195,9 @@
T_HAS_CUSTOM_TRACE = 0x200000
T_HAS_OLDSTYLE_FINALIZER = 0x400000
T_HAS_GCPTR = 0x1000000
-T_KEY_MASK = intmask(0xFE000000) # bug detection only
-T_KEY_VALUE = intmask(0x5A000000) # bug detection only
+T_HAS_MEMORY_PRESSURE = 0x2000000 # first field is memory pressure field
+T_KEY_MASK = intmask(0xFC000000) # bug detection only
+T_KEY_VALUE = intmask(0x58000000) # bug detection only
def _check_valid_type_info(p):
ll_assert(p.infobits & T_KEY_MASK == T_KEY_VALUE, "invalid type_id")
@@ -192,6 +212,25 @@
ll_assert(llop.is_group_member_nonzero(lltype.Bool, typeid),
"invalid type_id")
+def has_special_memory_pressure(TYPE):
+ if TYPE._is_varsize():
+ return False
+ T = TYPE
+ while True:
+ if 'special_memory_pressure' in T._flds:
+ return True
+ if 'super' not in T._flds:
+ return False
+ T = T._flds['super']
+
+def get_memory_pressure_ofs(TYPE):
+ T = TYPE
+ while True:
+ if 'special_memory_pressure' in T._flds:
+ return llmemory.offsetof(T, 'special_memory_pressure')
+ if 'super' not in T._flds:
+ assert False, "get_ and has_memory_pressure disagree"
+ T = T._flds['super']
def encode_type_shape(builder, info, TYPE, index):
"""Encode the shape of the TYPE into the TYPE_INFO structure 'info'."""
@@ -202,12 +241,18 @@
infobits |= T_HAS_GCPTR
#
fptrs = builder.special_funcptr_for_type(TYPE)
- if fptrs:
+ if fptrs or has_special_memory_pressure(TYPE):
+ customdata = lltype.malloc(GCData.CUSTOM_DATA_STRUCT, flavor='raw',
+ immortal=True)
+ info.customdata = customdata
if "destructor" in fptrs:
- info.customfunc = fptrs["destructor"]
+ customdata.customfunc = fptrs["destructor"]
if "old_style_finalizer" in fptrs:
- info.customfunc = fptrs["old_style_finalizer"]
+ customdata.customfunc = fptrs["old_style_finalizer"]
infobits |= T_HAS_OLDSTYLE_FINALIZER
+ if has_special_memory_pressure(TYPE):
+ infobits |= T_HAS_MEMORY_PRESSURE
+ info.customdata.memory_pressure_offset =
get_memory_pressure_ofs(TYPE)
#
if not TYPE._is_varsize():
info.fixedsize = llarena.round_up_for_allocation(
diff --git a/rpython/memory/gcwrapper.py b/rpython/memory/gcwrapper.py
--- a/rpython/memory/gcwrapper.py
+++ b/rpython/memory/gcwrapper.py
@@ -83,9 +83,9 @@
def gettypeid(self, obj):
return self.get_type_id(lltype.typeOf(obj).TO)
- def add_memory_pressure(self, size):
+ def add_memory_pressure(self, size, adr):
if hasattr(self.gc, 'raw_malloc_memory_pressure'):
- self.gc.raw_malloc_memory_pressure(size)
+ self.gc.raw_malloc_memory_pressure(size, adr)
def shrink_array(self, p, smallersize):
if hasattr(self.gc, 'shrink_array'):
diff --git a/rpython/rlib/rgc.py b/rpython/rlib/rgc.py
--- a/rpython/rlib/rgc.py
+++ b/rpython/rlib/rgc.py
@@ -598,21 +598,31 @@
return False
return type(x).__module__ != '__builtin__' # keep non-builtins
-def add_memory_pressure(estimate):
+def add_memory_pressure(estimate, object=None):
"""Add memory pressure for OpaquePtrs."""
pass
class AddMemoryPressureEntry(ExtRegistryEntry):
_about_ = add_memory_pressure
- def compute_result_annotation(self, s_nbytes):
+ def compute_result_annotation(self, s_nbytes, s_object=None):
from rpython.annotator import model as annmodel
+ if s_object is not None:
+ if not isinstance(s_object, annmodel.SomeInstance):
+ raise Exception("Wrong kind of object passed to "
+ "add memory pressure")
+ self.bookkeeper.memory_pressure_types.add(s_object.classdef)
return annmodel.s_None
def specialize_call(self, hop):
- [v_size] = hop.inputargs(lltype.Signed)
+ v_size = hop.inputarg(lltype.Signed, 0)
+ if len(hop.args_v) == 2:
+ v_obj = hop.inputarg(hop.args_r[1], 1)
+ args = [v_size, v_obj]
+ else:
+ args = [v_size]
hop.exception_cannot_occur()
- return hop.genop('gc_add_memory_pressure', [v_size],
+ return hop.genop('gc_add_memory_pressure', args,
resulttype=lltype.Void)
@@ -640,6 +650,15 @@
else:
return id(gcref._x)
+(TOTAL_MEMORY, TOTAL_ALLOCATED_MEMORY, TOTAL_MEMORY_PRESSURE,
+ PEAK_MEMORY, PEAK_ALLOCATED_MEMORY) = range(5)
+
+@not_rpython
+def get_stats(stat_no):
+ """ Long docstring goes here
+ """
+ raise NotImplementedError
+
@not_rpython
def dump_rpy_heap(fd):
raise NotImplementedError
@@ -834,6 +853,18 @@
return hop.genop('gc_get_rpy_type_index', vlist,
resulttype = hop.r_result)
+class Entry(ExtRegistryEntry):
+ _about_ = get_stats
+ def compute_result_annotation(self, s_no):
+ from rpython.annotator.model import SomeInteger
+ if not isinstance(s_no, SomeInteger):
+ raise Exception("expecting an integer")
+ return SomeInteger()
+ def specialize_call(self, hop):
+ args = hop.inputargs(lltype.Signed)
+ hop.exception_cannot_occur()
+ return hop.genop('gc_get_stats', args, resulttype=lltype.Signed)
+
@not_rpython
def _is_rpy_instance(gcref):
raise NotImplementedError
diff --git a/rpython/rlib/rthread.py b/rpython/rlib/rthread.py
--- a/rpython/rlib/rthread.py
+++ b/rpython/rlib/rthread.py
@@ -85,7 +85,11 @@
def allocate_lock():
- return Lock(allocate_ll_lock())
+ # Add some memory pressure for the size of the lock because it is an
+ # Opaque object
+ lock = Lock(allocate_ll_lock())
+ rgc.add_memory_pressure(TLOCKP_SIZE, lock)
+ return lock
@specialize.arg(0)
def ll_start_new_thread(func):
@@ -248,9 +252,6 @@
if rffi.cast(lltype.Signed, res) <= 0:
lltype.free(ll_lock, flavor='raw', track_allocation=False)
raise error("out of resources")
- # Add some memory pressure for the size of the lock because it is an
- # Opaque object
- rgc.add_memory_pressure(TLOCKP_SIZE)
return ll_lock
def free_ll_lock(ll_lock):
diff --git a/rpython/rlib/rzlib.py b/rpython/rlib/rzlib.py
--- a/rpython/rlib/rzlib.py
+++ b/rpython/rlib/rzlib.py
@@ -269,7 +269,6 @@
compress data.
"""
stream = lltype.malloc(z_stream, flavor='raw', zero=True)
- rgc.add_memory_pressure(rffi.sizeof(z_stream))
err = _deflateInit2(stream, level, method, wbits, memLevel, strategy)
if err == Z_OK:
if zdict is not None:
@@ -304,7 +303,6 @@
decompress data.
"""
stream = lltype.malloc(z_stream, flavor='raw', zero=True)
- rgc.add_memory_pressure(rffi.sizeof(z_stream))
err = _inflateInit2(stream, wbits)
if err == Z_OK:
if zdict is not None and wbits < 0:
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
@@ -327,6 +327,17 @@
assert not arena_addr.arena.objectptrs
arena_addr.arena.mark_freed()
+def arena_mmap(nbytes):
+ """Allocate and return a new arena, zero-initialized by the
+ system, calling mmap()."""
+ return arena_malloc(nbytes, True)
+
+def arena_munmap(arena_addr, nbytes):
+ """Release an arena allocated with arena_mmap()."""
+ arena_free(arena_addr)
+ assert nbytes == arena_addr.arena.nbytes
+
+
def arena_reset(arena_addr, size, zero):
"""Free all objects in the arena, which can then be reused.
This can also be used on a subrange of the arena.
@@ -530,6 +541,31 @@
llfakeimpl=arena_free,
sandboxsafe=True)
+def llimpl_arena_mmap(nbytes):
+ from rpython.rlib import rmmap
+ flags = rmmap.MAP_PRIVATE | rmmap.MAP_ANONYMOUS
+ prot = rmmap.PROT_READ | rmmap.PROT_WRITE
+ p = rffi.cast(llmemory.Address, rmmap.c_mmap_safe(
+ lltype.nullptr(rmmap.PTR.TO), nbytes, prot, flags, -1, 0))
+ if p == rffi.cast(llmemory.Address, -1):
+ p = rffi.cast(llmemory.Address, 0)
+ return p
+register_external(arena_mmap, [int], llmemory.Address,
+ 'll_arena.arena_mmap',
+ llimpl=llimpl_arena_mmap,
+ llfakeimpl=arena_mmap,
+ sandboxsafe=True)
+
+def llimpl_arena_munmap(arena_addr, nbytes):
+ from rpython.rlib import rmmap
+ assert nbytes >= 0
+ rmmap.c_munmap_safe(rffi.cast(rmmap.PTR, arena_addr), nbytes)
+register_external(arena_munmap, [llmemory.Address, int], None,
+ 'll_arena.arena_munmap',
+ llimpl=llimpl_arena_munmap,
+ llfakeimpl=arena_munmap,
+ sandboxsafe=True)
+
def llimpl_arena_reset(arena_addr, size, zero):
if zero:
if zero == 1:
diff --git a/rpython/rtyper/lltypesystem/lloperation.py
b/rpython/rtyper/lltypesystem/lloperation.py
--- a/rpython/rtyper/lltypesystem/lloperation.py
+++ b/rpython/rtyper/lltypesystem/lloperation.py
@@ -485,6 +485,7 @@
'gc_gettypeid' : LLOp(),
'gc_gcflag_extra' : LLOp(),
'gc_add_memory_pressure': LLOp(),
+ 'gc_get_stats' : LLOp(),
'gc_fq_next_dead' : LLOp(),
'gc_fq_register' : LLOp(),
'gc_ignore_finalizer' : LLOp(canrun=True),
diff --git a/rpython/rtyper/rclass.py b/rpython/rtyper/rclass.py
--- a/rpython/rtyper/rclass.py
+++ b/rpython/rtyper/rclass.py
@@ -15,6 +15,7 @@
RuntimeTypeInfo, getRuntimeTypeInfo, typeOf, Void, FuncType, Bool, Signed,
functionptr, attachRuntimeTypeInfo)
from rpython.rtyper.lltypesystem.lloperation import llop
+from rpython.rtyper.llannotation import lltype_to_annotation
from rpython.rtyper.llannotation import SomePtr
from rpython.rtyper.lltypesystem import rstr
from rpython.rtyper.rmodel import (
@@ -475,6 +476,13 @@
self.lowleveltype = Ptr(self.object_type)
self.gcflavor = gcflavor
+ def has_special_memory_pressure(self, tp):
+ if 'special_memory_pressure' in tp._flds:
+ return True
+ if 'super' in tp._flds:
+ return self.has_special_memory_pressure(tp._flds['super'])
+ return False
+
def _setup_repr(self, llfields=None, hints=None, adtmeths=None):
# NOTE: don't store mutable objects like the dicts below on 'self'
# before they are fully built, to avoid strange bugs in case
@@ -523,6 +531,16 @@
if not attrdef.readonly and self.is_quasi_immutable(name):
llfields.append(('mutate_' + name, OBJECTPTR))
+ bookkeeper = self.rtyper.annotator.bookkeeper
+ if self.classdef in bookkeeper.memory_pressure_types:
+ # we don't need to add it if it's already there for some of
+ # the parent type
+ if not
self.has_special_memory_pressure(self.rbase.object_type):
+ llfields.append(('special_memory_pressure', lltype.Signed))
+ fields['special_memory_pressure'] = (
+ 'special_memory_pressure',
+
self.rtyper.getrepr(lltype_to_annotation(lltype.Signed)))
+
object_type = MkStruct(self.classdef.name,
('super', self.rbase.object_type),
hints=hints,
@@ -663,6 +681,8 @@
while base.classdef is not None:
base = base.rbase
for fieldname in base.fields:
+ if fieldname == 'special_memory_pressure':
+ continue
try:
mangled, r = base._get_field(fieldname)
except KeyError:
@@ -717,6 +737,9 @@
resulttype=Ptr(self.object_type))
ctypeptr = inputconst(CLASSTYPE, self.rclass.getvtable())
self.setfield(vptr, '__class__', ctypeptr, llops)
+ if self.has_special_memory_pressure(self.object_type):
+ self.setfield(vptr, 'special_memory_pressure',
+ inputconst(lltype.Signed, 0), llops)
# initialize instance attributes from their defaults from the class
if self.classdef is not None:
flds = self.allinstancefields.keys()
diff --git a/rpython/translator/backendopt/writeanalyze.py
b/rpython/translator/backendopt/writeanalyze.py
--- a/rpython/translator/backendopt/writeanalyze.py
+++ b/rpython/translator/backendopt/writeanalyze.py
@@ -65,6 +65,11 @@
elif op.opname == "gc_store_indexed":
if graphinfo is None or not graphinfo.is_fresh_malloc(op.args[0]):
return self._gc_store_indexed_result(op)
+ elif op.opname == 'gc_add_memory_pressure':
+ # special_memory_pressure would be overwritten by zero, because
+ # the JIT cannot see the field write, which is why we assume
+ # it can write anything
+ return top_set
return empty_set
def _array_result(self, TYPE):
diff --git a/rpython/translator/c/test/test_newgc.py
b/rpython/translator/c/test/test_newgc.py
--- a/rpython/translator/c/test/test_newgc.py
+++ b/rpython/translator/c/test/test_newgc.py
@@ -1613,7 +1613,7 @@
digest = ropenssl.EVP_get_digestbyname('sha1')
self.ctx = ropenssl.EVP_MD_CTX_new()
ropenssl.EVP_DigestInit(self.ctx, digest)
- rgc.add_memory_pressure(ropenssl.HASH_MALLOC_SIZE + 64)
+ rgc.add_memory_pressure(ropenssl.HASH_MALLOC_SIZE + 64, self)
def __del__(self):
ropenssl.EVP_MD_CTX_free(self.ctx)
@@ -1624,12 +1624,16 @@
am3 = am2
am2 = am1
am1 = A()
+ am1 = am2 = am3 = None
# what can we use for the res?
- return 0
+ for i in range(10):
+ gc.collect()
+ return rgc.get_stats(rgc.TOTAL_MEMORY_PRESSURE)
return f
def test_nongc_opaque_attached_to_gc(self):
res = self.run("nongc_opaque_attached_to_gc")
+ # the res is 0 for non-memory-pressure-accounting GC
assert res == 0
def define_limited_memory(self):
@@ -1668,6 +1672,38 @@
class TestIncrementalMiniMarkGC(TestMiniMarkGC):
gcpolicy = "incminimark"
+ def define_total_memory_pressure(cls):
+ class A(object):
+ def __init__(self):
+ rgc.add_memory_pressure(10, self)
+
+ def __del__(self):
+ pass
+
+ class B(A):
+ def __init__(self):
+ rgc.add_memory_pressure(10, self)
+
+ class C(A):
+ pass
+
+ class Glob(object):
+ pass
+ glob = Glob()
+
+ def f():
+ glob.l = [None] * 3
+ for i in range(10000):
+ glob.l[i % 3] = A()
+ glob.l[(i + 1) % 3] = B()
+ glob.l[(i + 2) % 3] = C()
+ return rgc.get_stats(rgc.TOTAL_MEMORY_PRESSURE)
+ return f
+
+ def test_total_memory_pressure(self):
+ res = self.run("total_memory_pressure")
+ assert res == 30 # total reachable is 3
+
def define_random_pin(self):
class A:
foo = None
_______________________________________________
pypy-commit mailing list
[email protected]
https://mail.python.org/mailman/listinfo/pypy-commit