Skip to content

Change PyMem_Malloc to use pymalloc allocator #70437

Closed

Description

@vstinner
BPO 26249
Nosy @rhettinger, @pitrou, @vstinner, @serhiy-storchaka, @catalin-manciu
Files
  • pymem.patch
  • python_memleak.py
  • tu_malloc.c
  • pymem_27.patch
  • pymalloc.patch
  • Note: these values reflect the state of the issue at the time it was migrated and might not reflect the current state.

    Show more details

    GitHub fields:

    assignee = None
    closed_at = <Date 2016-04-26.11:37:12.570>
    created_at = <Date 2016-01-31.17:48:24.357>
    labels = ['performance']
    title = 'Change PyMem_Malloc to use pymalloc allocator'
    updated_at = <Date 2016-04-26.11:37:12.569>
    user = 'https://github.com/vstinner'

    bugs.python.org fields:

    activity = <Date 2016-04-26.11:37:12.569>
    actor = 'vstinner'
    assignee = 'none'
    closed = True
    closed_date = <Date 2016-04-26.11:37:12.570>
    closer = 'vstinner'
    components = []
    creation = <Date 2016-01-31.17:48:24.357>
    creator = 'vstinner'
    dependencies = []
    files = ['41767', '41778', '41779', '42004', '42158']
    hgrepos = []
    issue_num = 26249
    keywords = ['patch']
    message_count = 51.0
    messages = ['259290', '259297', '259376', '259377', '259378', '259379', '259382', '259383', '259384', '259385', '259389', '259390', '259391', '259392', '259393', '259395', '259440', '259441', '259445', '260674', '260675', '260681', '261430', '261431', '261433', '261445', '261446', '261447', '261448', '261449', '261450', '261452', '261453', '261454', '261455', '261456', '261457', '261458', '261459', '261488', '261749', '261766', '261788', '264020', '264027', '264130', '264132', '264174', '264245', '264251', '264252']
    nosy_count = 8.0
    nosy_names = ['rhettinger', 'pitrou', 'vstinner', 'python-dev', 'jtaylor', 'serhiy.storchaka', 'alecsandru.patrascu', 'catalin.manciu']
    pr_nums = []
    priority = 'normal'
    resolution = 'fixed'
    stage = None
    status = 'closed'
    superseder = None
    type = 'performance'
    url = 'https://bugs.python.org/issue26249'
    versions = ['Python 3.6']

    Activity

    1. vstinner commented on Jan 31, 2016

      @vstinner
      MemberAuthor

      The issue bpo-23601 showed speedup for the dict type by replacing PyMem_Malloc() with PyObject_Malloc() in dictobject.c.

      When I worked on the PEP-445, it was discussed to use the Python fast memory allocator for small memory allocations (<= 512 bytes), but I think that nobody tested on benchmark.

      So I open an issue to discuss that.

      By the way, we should also benchmark the Windows memory allocator which limits fragmentations. Maybe we can skip the Python small memory allocator on recent version of Windows?

      Attached patch implements the change. The main question is the speedup on various kinds of memory allocations (need a benchmark) :-)

      I will try to run benchmarks.

      --

      If the patch slows down Python, maybe we can investigate if some Python types (like dict) mostly uses "small" memory blocks (<= 512 bytes).

    2. vstinner commented on Jan 31, 2016

      @vstinner
      MemberAuthor

      Ok, to avoid confusion, I opened an issue specific to Windows for its "Low-fragmentation Heap": issue bpo-26251.

      Other issues related to memory allocators.

      Merged:

      • issue bpo-21233: Add *Calloc functions to CPython memory allocation API (extension of the PEP-445, asked by numpy)
      • issue bpo-13483: Use VirtualAlloc to allocate memory arenas (implementation of the PEP-445)
      • issue bpo-3329: API for setting the memory allocator used by Python

      Open:

      • issue bpo-18835: Add aligned memory variants to the suite of PyMem functions/macros => this one is still open, the status is unclear :-/
    3. pitrou commented on Feb 2, 2016

      @pitrou
      Member

      Hum, the point of PyMem_Malloc() is that it's distinct from PyObject_Malloc(), right? Why would you redirect one to the other?

    4. pitrou commented on Feb 2, 2016

      @pitrou
      Member

      (of course, we might question why we have two different families of allocation APIs...)

    5. vstinner commented on Feb 2, 2016

      @vstinner
      MemberAuthor

      Hum, the point of PyMem_Malloc() is that it's distinct from PyObject_Malloc(), right? Why would you redirect one to the other?

      For performances.

      (of course, we might question why we have two different families of allocation APIs...)

      That's the real question: why does Python have PyMem family? Is it still justified in 2016?

      --

      Firefox uses jemalloc to limit the fragmentation of the heap memory. Once I spent a lot of time to try to understand the principle of fragmentation, and in my tiny benchmarks, jemalloc was *much* better than system allocator. By the way, jemalloc scales well on multiple threads ;-)

      My notes on heap memory fragmentation: http://haypo-notes.readthedocs.org/heap_fragmentation.html

    6. vstinner commented on Feb 2, 2016

      @vstinner
      MemberAuthor

      About heap memory fragmentation, see also my attached two "benchmarks" in Python and C: python_memleak.py and tu_malloc.c.

    7. vstinner commented on Feb 2, 2016

      @vstinner
      MemberAuthor

      So, I ran ssh://hg@hg.python.org/benchmarks with my patch. It looks like some benchmarks are up to 4% faster:

      $ python3 -u perf.py ../default/python.orig ../default/python.pymem

      INFO:root:Automatically selected timer: perf_counter
      [ 1/10] 2to3...
      INFO:root:Running ../default/python.pymem lib3/2to3/2to3 -f all lib/2to3
      INFO:root:Running ../default/python.pymem lib3/2to3/2to3 -f all lib/2to3 1 time
      INFO:root:Running ../default/python.orig lib3/2to3/2to3 -f all lib/2to3
      INFO:root:Running ../default/python.orig lib3/2to3/2to3 -f all lib/2to3 1 time
      [ 2/10] chameleon_v2...
      INFO:root:Running ../default/python.pymem performance/bm_chameleon_v2.py -n 50 --timer perf_counter
      INFO:root:Running ../default/python.orig performance/bm_chameleon_v2.py -n 50 --timer perf_counter
      [ 3/10] django_v3...
      INFO:root:Running ../default/python.pymem performance/bm_django_v3.py -n 50 --timer perf_counter
      INFO:root:Running ../default/python.orig performance/bm_django_v3.py -n 50 --timer perf_counter
      [ 4/10] fastpickle...
      INFO:root:Running ../default/python.pymem performance/bm_pickle.py -n 50 --timer perf_counter --use_cpickle pickle
      INFO:root:Running ../default/python.orig performance/bm_pickle.py -n 50 --timer perf_counter --use_cpickle pickle
      [ 5/10] fastunpickle...
      INFO:root:Running ../default/python.pymem performance/bm_pickle.py -n 50 --timer perf_counter --use_cpickle unpickle
      INFO:root:Running ../default/python.orig performance/bm_pickle.py -n 50 --timer perf_counter --use_cpickle unpickle
      [ 6/10] json_dump_v2...
      INFO:root:Running ../default/python.pymem performance/bm_json_v2.py -n 50 --timer perf_counter
      INFO:root:Running ../default/python.orig performance/bm_json_v2.py -n 50 --timer perf_counter
      [ 7/10] json_load...
      INFO:root:Running ../default/python.pymem performance/bm_json.py -n 50 --timer perf_counter json_load
      INFO:root:Running ../default/python.orig performance/bm_json.py -n 50 --timer perf_counter json_load
      [ 8/10] nbody...
      INFO:root:Running ../default/python.pymem performance/bm_nbody.py -n 50 --timer perf_counter
      INFO:root:Running ../default/python.orig performance/bm_nbody.py -n 50 --timer perf_counter
      [ 9/10] regex_v8...
      INFO:root:Running ../default/python.pymem performance/bm_regex_v8.py -n 50 --timer perf_counter
      INFO:root:Running ../default/python.orig performance/bm_regex_v8.py -n 50 --timer perf_counter
      [10/10] tornado_http...
      INFO:root:Running ../default/python.pymem performance/bm_tornado_http.py -n 100 --timer perf_counter
      INFO:root:Running ../default/python.orig performance/bm_tornado_http.py -n 100 --timer perf_counter

      Report on Linux smithers 4.3.3-300.fc23.x86_64 #1 SMP Tue Jan 5 23:31:01 UTC 2016 x86_64 x86_64
      Total CPU cores: 8

      ### 2to3 ###
      6.880090 -> 6.818911: 1.01x faster

      ### fastpickle ###
      Min: 0.453826 -> 0.442081: 1.03x faster
      Avg: 0.456499 -> 0.443978: 1.03x faster
      Significant (t=20.03)
      Stddev: 0.00370 -> 0.00242: 1.5293x smaller

      ### fastunpickle ###
      Min: 0.547908 -> 0.526027: 1.04x faster
      Avg: 0.554663 -> 0.528686: 1.05x faster
      Significant (t=15.95)
      Stddev: 0.00893 -> 0.00728: 1.2260x smaller

      ### json_dump_v2 ###
      Min: 2.733907 -> 2.627718: 1.04x faster
      Avg: 2.762473 -> 2.664675: 1.04x faster
      Significant (t=11.99)
      Stddev: 0.03796 -> 0.04341: 1.1435x larger

      ### regex_v8 ###
      Min: 0.042438 -> 0.042581: 1.00x slower
      Avg: 0.042805 -> 0.044078: 1.03x slower
      Significant (t=-2.12)
      Stddev: 0.00171 -> 0.00388: 2.2694x larger

      ### tornado_http ###
      Min: 0.254089 -> 0.246088: 1.03x faster
      Avg: 0.257046 -> 0.249033: 1.03x faster
      Significant (t=15.83)
      Stddev: 0.00401 -> 0.00310: 1.2930x smaller

      The following not significant results are hidden, use -v to show them:
      chameleon_v2, django_v3, json_load, nbody.

      real 19m13.413s
      user 18m50.024s
      sys 0m22.507s

    8. YurySelivanov commented on Feb 2, 2016

      YurySelivanovmannequin
      Mannequin

      On Feb 2, 2016, at 7:00 AM, STINNER Victor <report@bugs.python.org> wrote:

      So, I ran ssh://hg@hg.python.org/benchmarks with my patch. It looks like some benchmarks are up to 4% faster:

      Please use -r flag for perf.py

    9. pitrou commented on Feb 2, 2016

      @pitrou
      Member

      It looks like some benchmarks are up to 4% faster:

      What this says is that some internals uses of PyMem_XXX should be replaced with PyObject_XXX.

    10. vstinner commented on Feb 2, 2016

      @vstinner
      MemberAuthor

      FYI benchmark result to compare Python with and without pymalloc (fast memory allocator for block <= 512 bytes). As expected, no pymalloc is slower, up to 30% slower (and it's never faster).

      Report on Linux smithers 4.3.3-300.fc23.x86_64 #1 SMP Tue Jan 5 23:31:01 UTC 2016 x86_64 x86_64
      Total CPU cores: 8

      ### 2to3 ###
      7.253671 -> 7.558993: 1.04x slower

      ### chameleon_v2 ###
      Min: 5.598481 -> 5.794526: 1.04x slower
      Avg: 5.714233 -> 5.922142: 1.04x slower
      Significant (t=-8.01)
      Stddev: 0.15956 -> 0.09048: 1.7636x smaller

      ### django_v3 ###
      Min: 0.574221 -> 0.606462: 1.06x slower
      Avg: 0.579659 -> 0.612088: 1.06x slower
      Significant (t=-28.44)
      Stddev: 0.00605 -> 0.00532: 1.1371x smaller

      ### fastpickle ###
      Min: 0.450852 -> 0.502645: 1.11x slower
      Avg: 0.455619 -> 0.513777: 1.13x slower
      Significant (t=-26.24)
      Stddev: 0.00696 -> 0.01404: 2.0189x larger

      ### fastunpickle ###
      Min: 0.544064 -> 0.696306: 1.28x slower
      Avg: 0.552459 -> 0.705372: 1.28x slower
      Significant (t=-85.52)
      Stddev: 0.00798 -> 0.00980: 1.2281x larger

      ### json_dump_v2 ###
      Min: 2.780312 -> 3.265531: 1.17x slower
      Avg: 2.830463 -> 3.370060: 1.19x slower
      Significant (t=-23.73)
      Stddev: 0.04190 -> 0.15521: 3.7046x larger

      ### json_load ###
      Min: 0.428893 -> 0.558956: 1.30x slower
      Avg: 0.431941 -> 0.569441: 1.32x slower
      Significant (t=-74.76)
      Stddev: 0.00791 -> 0.01033: 1.3060x larger

      ### regex_v8 ###
      Min: 0.043439 -> 0.044614: 1.03x slower
      Avg: 0.044388 -> 0.046487: 1.05x slower
      Significant (t=-4.95)
      Stddev: 0.00215 -> 0.00209: 1.0283x smaller

      ### tornado_http ###
      Min: 0.264603 -> 0.278840: 1.05x slower
      Avg: 0.270153 -> 0.285263: 1.06x slower
      Significant (t=-23.04)
      Stddev: 0.00489 -> 0.00436: 1.1216x smaller

      The following not significant results are hidden, use -v to show them:
      nbody.

    11. vstinner commented on Feb 2, 2016

      @vstinner
      MemberAuthor

      Test with jemalloc using the shell script "python.jemalloc":
      ---
      #!/bin/sh
      LD_PRELOAD=/usr/lib64/libjemalloc.so /home/haypo/prog/python/default/python "$@"
      ---

      Memory consumption:
      python3 -u perf.py -m ../default/python ../default/python.jemalloc

      Report on Linux smithers 4.3.3-300.fc23.x86_64 #1 SMP Tue Jan 5 23:31:01 UTC 2016 x86_64 x86_64
      Total CPU cores: 8

      ### 2to3 ###
      Mem max: 43100.000 -> 220.000: 195.9091x smaller

      ### chameleon_v2 ###
      Mem max: 367276.000 -> 224.000: 1639.6250x smaller

      ### django_v3 ###
      Mem max: 24136.000 -> 284.000: 84.9859x smaller

      ### fastpickle ###
      Mem max: 8692.000 -> 284.000: 30.6056x smaller

      ### fastunpickle ###
      Mem max: 8704.000 -> 216.000: 40.2963x smaller

      ### json_dump_v2 ###
      Mem max: 10448.000 -> 216.000: 48.3704x smaller

      ### json_load ###
      Mem max: 8444.000 -> 220.000: 38.3818x smaller

      ### nbody ###
      Mem max: 7388.000 -> 220.000: 33.5818x smaller

      ### regex_v8 ###
      Mem max: 12764.000 -> 220.000: 58.0182x smaller

      ### tornado_http ###
      Mem max: 28216.000 -> 228.000: 123.7544x smaller


      Performance:

      Report on Linux smithers 4.3.3-300.fc23.x86_64 #1 SMP Tue Jan 5 23:31:01 UTC 2016 x86_64 x86_64
      Total CPU cores: 8

      ### 2to3 ###
      7.413484 -> 7.189792: 1.03x faster

      ### chameleon_v2 ###
      Min: 5.559697 -> 5.869468: 1.06x slower
      Avg: 5.672448 -> 6.033152: 1.06x slower
      Significant (t=-13.67)
      Stddev: 0.12098 -> 0.14203: 1.1740x larger

      ### nbody ###
      Min: 0.242194 -> 0.229747: 1.05x faster
      Avg: 0.244991 -> 0.235297: 1.04x faster
      Significant (t=9.75)
      Stddev: 0.00262 -> 0.00652: 2.4861x larger

      ### regex_v8 ###
      Min: 0.042532 -> 0.046920: 1.10x slower
      Avg: 0.043249 -> 0.047907: 1.11x slower
      Significant (t=-13.23)
      Stddev: 0.00180 -> 0.00172: 1.0503x smaller

      ### tornado_http ###
      Min: 0.265755 -> 0.274526: 1.03x slower
      Avg: 0.273617 -> 0.284186: 1.04x slower
      Significant (t=-6.67)
      Stddev: 0.00583 -> 0.01474: 2.5297x larger

      The following not significant results are hidden, use -v to show them:
      django_v3, fastpickle, fastunpickle, json_dump_v2, json_load.

    12. vstinner commented on Feb 2, 2016

      @vstinner
      MemberAuthor

      > It looks like some benchmarks are up to 4% faster:

      What this says is that some internals uses of PyMem_XXX should be replaced with PyObject_XXX.

      Why not changing PyMem_XXX to use the same fast allocator than PyObject_XXX? (as proposed in this issue)

      FYI we now also have the PyMem_RawXXX family :)

    13. pitrou commented on Feb 2, 2016

      @pitrou
      Member

      Le 02/02/2016 15:47, STINNER Victor a écrit :

      2to3

      Mem max: 43100.000 -> 220.000: 195.9091x smaller

      chameleon_v2

      Mem max: 367276.000 -> 224.000: 1639.6250x smaller

      django_v3

      Mem max: 24136.000 -> 284.000: 84.9859x smaller

      These figures are not even remotely believable.
      It would make sense to investigate them before posting such numbers ;-)

    14. pitrou commented on Feb 2, 2016

      @pitrou
      Member

      Le 02/02/2016 15:48, STINNER Victor a écrit :

      > What this says is that some internals uses of PyMem_XXX should be replaced with PyObject_XXX.

      Why not changing PyMem_XXX to use the same fast allocator than
      PyObject_XXX? (as proposed in this issue)

      Why have two sets of functions doing exactly the same thing?

    15. 27 remaining items

    16. vstinner commented on Mar 14, 2016

      @vstinner
      MemberAuthor

      pymalloc.patch: Updated patch.

    17. vstinner commented on Mar 14, 2016

      @vstinner
      MemberAuthor

      Using the issue bpo-26516 (PYTHONMALLOC=debug), we can check PyGILState_Check() at runtime, but there is currently an issue related to sub-interpreters. The assertion fails in support.run_in_subinterp(), function used by test_threading and test_capi for example.

      I created bpo-26558 to implement GIL checks in PyMem_Malloc() and PyObject_Malloc().

    18. vstinner commented on Mar 14, 2016

      @vstinner
      MemberAuthor

      I created the issue bpo-26563 "PyMem_Malloc(): check that the GIL is hold in debug hooks".

    19. python-dev commented on Apr 22, 2016

      python-devmannequin
      Mannequin

      New changeset 68b2a43d8653 by Victor Stinner in branch 'default':
      PyMem_Malloc() now uses the fast pymalloc allocator
      https://hg.python.org/cpython/rev/68b2a43d8653

    20. python-dev commented on Apr 22, 2016

      python-devmannequin
      Mannequin

      New changeset 104ed24ebbd0 by Victor Stinner in branch 'default':
      Issue bpo-26249: Try test_capi on Windows
      https://hg.python.org/cpython/rev/104ed24ebbd0

    21. python-dev commented on Apr 24, 2016

      python-devmannequin
      Mannequin

      New changeset 7acad5d8f80e by Victor Stinner in branch 'default':
      Issue bpo-26249: Mention PyMem_Malloc() change in What's New in Python 3.6 in the
      https://hg.python.org/cpython/rev/7acad5d8f80e

    22. vstinner commented on Apr 24, 2016

      @vstinner
      MemberAuthor

      I documented the change, buildbots are happy, I close the issue.

    23. serhiy-storchaka commented on Apr 25, 2016

      @serhiy-storchaka
      Member

      68b2a43d8653 introduced memory leak.

      $ ./python -m test.regrtest -uall -R : test_format
      Run tests sequentially
      0:00:00 [1/1] test_format
      beginning 9 repetitions
      123456789
      .........
      test_format leaked [6, 7, 7, 7] memory blocks, sum=27
      1 test failed:
          test_format
      Total duration: 0:00:01
    24. python-dev commented on Apr 26, 2016

      python-devmannequin
      Mannequin

      New changeset 090502a0c69c by Victor Stinner in branch 'default':
      Issue bpo-25349, bpo-26249: Fix memleak in formatfloat()
      https://hg.python.org/cpython/rev/090502a0c69c

    25. vstinner commented on Apr 26, 2016

      @vstinner
      MemberAuthor

      68b2a43d8653 introduced memory leak.

      I was very surprised to see a regression in test_format since I didn't change any change related to bytes, bytearray or str formatting in this issue.

      In fact, it's much better than that! With PyMem_Malloc() using pymalloc, we benefit for free of the cheap "_Py_AllocatedBlocks" memory leak detector. I introduced the memory leak in the issue bpo-25349 when I optimimzed bytes%args and bytearray%args using the new _PyBytesWriter API.

      This memory leak gave me an idea, I opened the issue bpo-26850: "PyMem_RawMalloc(): update also sys.getallocatedblocks() in debug mode".

    26. vstinner commented on Apr 26, 2016

      @vstinner
      MemberAuthor

      There are no more know bugs related to this change, I close the issue. Thanks for the test_format report Serhiy, I missed it.

    27. transferred this issue fromon Apr 10, 2022
    Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

    Metadata

    Metadata

    Assignees

    No one assigned

      Labels

      performancePerformance or resource usage

      Projects

      No projects

        Milestone

        No milestone

        Relationships

        None yet

        Development

        No branches or pull requests

        Issue actions