Sitelet https://github.com/python/cpython/issues/129711
Skip to content

json.dump(x,f) is much slower than f.write(json.dumps(x)) #129711

Description

@wjmelements

Bug report

Bug description:

Experimentally I measured a huge performance improvement when I switched my code from

json.dump(x, f, **)

to

f.write(json.dumps(x, **))

Method

I essentially wrote the same contents to different files sequentially and measured the total amount of time taken. The json contents had 1, 300, and 400 entries per level, and 1, 5, and 6 levels of depth. There's quite a level of variance here but this wasn't what I was trying to measure in the first place. I discovered this by chance, so forgive the lack of precision. I also don't have the source code anymore because I wasn't originally planning to report this discovery.

Results

File Size Consecutive Files dump µs dumps µs
74 1 508 581
74 2 520 541
74 4 1153 1151
74 8 1930 1750
39184 1 6363 1086
39184 2 11261 1821
39184 4 38126 3521
39184 8 80411 6466
468218 1 82821 11921
468218 2 150234 38017
468218 4 302357 42137
468218 8 573450 78545

Conclusion

A cursory investigation into the cpython code suggests that the slow part is the sequential writing of the iterencode yield. The chunks are quite small.

CPython versions tested on:

3.10

Operating systems tested on:

macOS

Linked PRs

Activity

  1. pochmann3 commented on Feb 6, 2025

    @pochmann3
    Contributor

    Old issue that I think partially covers this (wasn't about depth, though):

    #56343

  2. pochmann3 commented on Feb 6, 2025

    @pochmann3
    Contributor

    Demo with deep nesting:

     37.3 ms  json.dump(x, f)
      0.3 ms  f.write(json.dumps(x))
     36.2 ms  json.dump(x, f)
      0.3 ms  f.write(json.dumps(x))
     36.2 ms  json.dump(x, f)
      0.3 ms  f.write(json.dumps(x))
    
    from timeit import timeit
    
    setup = '''
    import io, json
    f = io.StringIO()
    x = [0] * 1000
    for i in range(900):
        x = [x]
    '''
    
    for code in [
        'json.dump(x, f)',
        'f.write(json.dumps(x))',
    ] * 3:
        t = timeit(code, setup, number=10) * 100
        print(f'{t:5.1f} ms  {code}')

    Attempt This Online!

  3. added
    stdlibStandard Library Python modules in the Lib/ directory
    on Feb 7, 2025
  4. pganssle commented on Feb 7, 2025

    @pganssle
    Member

    I think the main difference between these code paths is that when you are incrementally encoding the JSON, CPython uses the Python version of make_encoder, whereas when the encoding is done all at once, CPython uses the C implementation from _json.c. If you modify @pochmann3's example to disable the C encoder:

    from timeit import timeit
    import json.encoder
    json.encoder.c_make_encoder = None
    
    setup = '''
    ...

    The two functions perform the same:

     37.9 ms  json.dump(x, f)
     37.3 ms  f.write(json.dumps(x))
     36.7 ms  json.dump(x, f)
     38.0 ms  f.write(json.dumps(x))
     35.0 ms  json.dump(x, f)
     35.4 ms  f.write(json.dumps(x))
    

    I'm guessing that the performance would be a lot closer by impementing an incremental encoder in C.

  5. pochmann3 commented on Feb 9, 2025

    @pochmann3
    Contributor

    the primary difference stems from json.dump performing incremental writes using Python's iterencode, whereas json.dumps utilizes the C-optimized encoder

    I haven't looked at the C version, but dump was that slow in my demo mostly because of the recursion, not because it uses Python.

  6. eendebakpt commented on Feb 13, 2025

    @eendebakpt
    Contributor

    @Ghost4 Performance related improvements are often a trade-off between performance and added code complexity. Here the gain is significant, but it is not yet clear how complex a C implementation would be. Also it is not entirely clear what the reason is for the performance differences: C vs. Pytho, recursion, or something else.

    Guidelines for making PRs can be found at https://devguide.python.org/

  7. added a commit that references this issue on Jun 15, 2026
  8. added 2 commits that reference this issue on Aug 11, 2026
  9. 5 remaining items

  10. added 11 commits that reference this issue on Aug 12, 2026
  11. added 4 commits that reference this issue on Aug 22, 2026
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

    extension-modulesC modules in the Modules dirperformancePerformance or resource usagestdlibStandard Library Python modules in the Lib/ directorytype-bugAn unexpected behavior, bug, or error

    Projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions