fastsimdjson 0.7.0__tar.gz → 0.9.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (25) hide show
  1. {fastsimdjson-0.7.0/src/fastsimdjson.egg-info → fastsimdjson-0.9.0}/PKG-INFO +50 -8
  2. {fastsimdjson-0.7.0 → fastsimdjson-0.9.0}/README.md +49 -7
  3. {fastsimdjson-0.7.0 → fastsimdjson-0.9.0}/pyproject.toml +1 -1
  4. {fastsimdjson-0.7.0 → fastsimdjson-0.9.0}/src/fastsimdjson.cpp +379 -38
  5. {fastsimdjson-0.7.0 → fastsimdjson-0.9.0/src/fastsimdjson.egg-info}/PKG-INFO +50 -8
  6. {fastsimdjson-0.7.0 → fastsimdjson-0.9.0}/tests/test_dumps.py +112 -0
  7. {fastsimdjson-0.7.0 → fastsimdjson-0.9.0}/vendor/simdjson.cpp +1473 -2778
  8. {fastsimdjson-0.7.0 → fastsimdjson-0.9.0}/vendor/simdjson.h +7839 -10951
  9. {fastsimdjson-0.7.0 → fastsimdjson-0.9.0}/MANIFEST.in +0 -0
  10. {fastsimdjson-0.7.0 → fastsimdjson-0.9.0}/setup.cfg +0 -0
  11. {fastsimdjson-0.7.0 → fastsimdjson-0.9.0}/setup.py +0 -0
  12. {fastsimdjson-0.7.0 → fastsimdjson-0.9.0}/src/fastsimdjson.egg-info/SOURCES.txt +0 -0
  13. {fastsimdjson-0.7.0 → fastsimdjson-0.9.0}/src/fastsimdjson.egg-info/dependency_links.txt +0 -0
  14. {fastsimdjson-0.7.0 → fastsimdjson-0.9.0}/src/fastsimdjson.egg-info/requires.txt +0 -0
  15. {fastsimdjson-0.7.0 → fastsimdjson-0.9.0}/src/fastsimdjson.egg-info/top_level.txt +0 -0
  16. {fastsimdjson-0.7.0 → fastsimdjson-0.9.0}/tests/test_files.py +0 -0
  17. {fastsimdjson-0.7.0 → fastsimdjson-0.9.0}/tests/test_lazy.py +0 -0
  18. {fastsimdjson-0.7.0 → fastsimdjson-0.9.0}/tests/test_loads.py +0 -0
  19. {fastsimdjson-0.7.0 → fastsimdjson-0.9.0}/tests/test_stream.py +0 -0
  20. {fastsimdjson-0.7.0 → fastsimdjson-0.9.0}/vendor/simdutf.cpp +0 -0
  21. {fastsimdjson-0.7.0 → fastsimdjson-0.9.0}/vendor/simdutf.h +0 -0
  22. {fastsimdjson-0.7.0 → fastsimdjson-0.9.0}/vendor/simdutf_c.h +0 -0
  23. {fastsimdjson-0.7.0 → fastsimdjson-0.9.0}/vendor/zmij-LICENSE +0 -0
  24. {fastsimdjson-0.7.0 → fastsimdjson-0.9.0}/vendor/zmij.cc +0 -0
  25. {fastsimdjson-0.7.0 → fastsimdjson-0.9.0}/vendor/zmij.h +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: fastsimdjson
3
- Version: 0.7.0
3
+ Version: 0.9.0
4
4
  Summary: Fast JSON parsing for Python, built on simdjson
5
5
  Author-email: Daniel Lemire <daniel@lemire.me>
6
6
  License-Expression: Apache-2.0
@@ -42,7 +42,8 @@ function is faster still. It also reads streams of documents (NDJSON, JSON
42
42
  Lines), on many threads with free-threaded Python, and can extract only the
43
43
  fields you need from each document. It writes JSON in two ways: `dumps` returns the same `str` as
44
44
  `json.dumps`, and `dumpb` returns the same `bytes` as `orjson.dumps`, as fast
45
- as orjson.
45
+ as orjson; `dumpb_many` writes NDJSON, on many threads with free-threaded
46
+ Python.
46
47
 
47
48
  With pip:
48
49
 
@@ -158,6 +159,30 @@ serialize dataclasses, `datetime`, `date`, `time`, `UUID`, numpy arrays or
158
159
  `OPT_PASSTHROUGH_DATACLASS` and `OPT_PASSTHROUGH_DATETIME` were set, and the
159
160
  options that concern only these types are accepted and have no effect.
160
161
 
162
+ ### `dumpb_many`: NDJSON, on several threads
163
+
164
+ ```python
165
+ data = fastsimdjson.dumpb_many(records) # b'{...}\n{...}\n'
166
+ data = fastsimdjson.dumpb_many(records, threads=16) # free-threaded Python
167
+ ```
168
+
169
+ `dumpb_many(records, default=None, option=None, *, threads=None)` returns
170
+ the records (a list, a tuple or any iterable) as NDJSON `bytes`: each record
171
+ as `dumpb` writes it, followed by a newline, so that the output reads back
172
+ with `loads_many(data, format="lines")`. `default` and `option` are those of
173
+ `dumpb`, except `OPT_INDENT_2`, which would spread a record over several
174
+ lines (`ValueError`); `OPT_APPEND_NEWLINE` changes nothing.
175
+
176
+ On free-threaded Python, `threads=N` writes chunks of records on N threads at
177
+ once (`threads=0` or `None`: one per processor), with the worker threads of
178
+ `map_many`. The output and the errors are those of one thread: when records
179
+ cannot be serialized, the error of the first of them is raised (`default` may
180
+ then have been called on later records, in other threads, and it must be
181
+ safe to call from several threads at once). With the GIL, the calling thread
182
+ does all the work. Like `dumpb`, `dumpb_many` does not crash when other
183
+ threads change the records meanwhile, but the output then reflects some of
184
+ the changes and not others.
185
+
161
186
  ### Files
162
187
 
163
188
  ```python
@@ -274,7 +299,7 @@ calling thread. Views returned by `parse` remain valid.
274
299
  ## Build and test
275
300
 
276
301
  Python 3.10 or newer, and a C++17 compiler (clang, GCC or MSVC). The simdjson
277
- 5.0.2 and simdutf 9.2.1 amalgamations, and zmij 1.2 (shortest float
302
+ 5.0.7 and simdutf 9.2.1 amalgamations, and zmij 1.2 (shortest float
278
303
  formatting, MIT license), are already in `vendor/`.
279
304
 
280
305
  pip:
@@ -380,7 +405,7 @@ geometric mean over the 22 files (higher is better).
380
405
 
381
406
  <picture>
382
407
  <source media="(prefers-color-scheme: dark)" srcset="https://raw.githubusercontent.com/simdjson/fastpysimdjson/main/doc/perf_loads_dark.png">
383
- <img src="https://raw.githubusercontent.com/simdjson/fastpysimdjson/main/doc/perf_loads.png" width="80%" alt="Parsing JSON in Python: fastsimdjson loads 0.78 GB/s, orjson 0.60, msgspec 0.53, jiter 0.48, pysimdjson 0.44, cysimdjson 0.43, ujson 0.37, python-rapidjson 0.24, simplejson 0.23, json 0.22">
408
+ <img src="https://raw.githubusercontent.com/simdjson/fastpysimdjson/main/doc/perf_loads.png" width="80%" alt="Parsing JSON in Python: fastsimdjson loads 0.78 GB/s, orjson 0.60, msgspec 0.53, jiter 0.48, jiter without its string cache 0.44, pysimdjson 0.44, cysimdjson 0.43, ujson 0.37, python-rapidjson 0.24, simplejson 0.23, json 0.22">
384
409
  </picture>
385
410
 
386
411
  | parser | GB/s | vs `json.loads` |
@@ -391,7 +416,8 @@ geometric mean over the 22 files (higher is better).
391
416
  | ujson 6.0.0 | 0.37 | 1.66× |
392
417
  | cysimdjson 26.27 | 0.43 | 1.96× |
393
418
  | pysimdjson 7.0.2 | 0.44 | 1.98× |
394
- | jiter 0.17.0 | 0.48 | 2.21× |
419
+ | jiter 0.17.0 (no string cache) | 0.44 | 2.00× |
420
+ | jiter 0.17.0 (string cache, the default) | 0.48 | 2.21× |
395
421
  | msgspec 0.22.0 | 0.53 | 2.42× |
396
422
  | orjson 3.12.0 | 0.60 | 2.74× |
397
423
  | **fastsimdjson `loads`** | **0.78** | **3.53×** |
@@ -407,7 +433,7 @@ the collector enabled and 1.22× with it disabled (geometric means); without
407
433
  the collector, `canada.json`, `mesh.json` and `numbers.json` are ties. yyjson 4.0.6 is left out: it
408
434
  returns wrong strings for non-ASCII text. jiter (the parser of pydantic)
409
435
  caches strings by default; turning the cache off (`cache_mode=False`) makes it
410
- slower here (0.44 GB/s).
436
+ slower here.
411
437
 
412
438
  Parsing is no longer the bottleneck. simdjson alone parses these files at
413
439
  3.1 GB/s. It accounts for about a third of the time of `loads`; the rest goes
@@ -422,12 +448,14 @@ and the screen name of the 100 statuses of `twitter.json`:
422
448
 
423
449
  <picture>
424
450
  <source media="(prefers-color-scheme: dark)" srcset="https://raw.githubusercontent.com/simdjson/fastpysimdjson/main/doc/perf_parse_dark.png">
425
- <img src="https://raw.githubusercontent.com/simdjson/fastpysimdjson/main/doc/perf_parse.png" width="80%" alt="Reading part of twitter.json: fastsimdjson parse 158 µs, pysimdjson 179, cysimdjson 229, msgspec 336, fastsimdjson loads 861, orjson 1009, json 3922">
451
+ <img src="https://raw.githubusercontent.com/simdjson/fastpysimdjson/main/doc/perf_parse.png" width="80%" alt="Reading part of twitter.json: fastsimdjson parse 158 µs, pysimdjson 179, cysimdjson 229, msgspec 336, fastsimdjson loads 861, orjson 1009, jiter 1281, jiter without its string cache 1592, json 3922">
426
452
  </picture>
427
453
 
428
454
  | method | µs |
429
455
  |---|---:|
430
456
  | `json.loads` | 3922 |
457
+ | jiter (no string cache) | 1592 |
458
+ | jiter (string cache) | 1281 |
431
459
  | orjson | 1009 |
432
460
  | fastsimdjson `loads` | 861 |
433
461
  | msgspec (typed `Struct`) | 336 |
@@ -481,6 +509,18 @@ times faster than msgspec and 10 times faster than `json`. The comparison was
481
509
  run on a processor with AVX-512, which `dumpb` and orjson both use to escape
482
510
  strings; other x64 processors use SSE2 and ARM processors NEON.
483
511
 
512
+ ### Writing NDJSON with `dumpb_many`
513
+
514
+ The 593,945 events of two hours of [GH Archive](https://www.gharchive.org/)
515
+ (1.87 GB of NDJSON), written back as NDJSON from Python objects. With the
516
+ GIL (Python 3.14.6), one thread: `orjson.dumps` on each record, joined, runs
517
+ at 0.94 GB/s, `dumpb` on each record at 1.07 GB/s, and `dumpb_many` at
518
+ 1.41 GB/s. On free-threaded Python 3.14.6t, GB/s:
519
+
520
+ | threads | 1 | 8 | 32 | 64 |
521
+ |---|---:|---:|---:|---:|
522
+ | `dumpb_many(records, threads=N)` | 1.3 | 6.0 | 11 | 12 |
523
+
484
524
  ### Streams with `loads_many`
485
525
 
486
526
  20 MB of NDJSON (5268 objects and arrays, one per line), made from the same
@@ -488,12 +528,14 @@ files; best of three runs:
488
528
 
489
529
  <picture>
490
530
  <source media="(prefers-color-scheme: dark)" srcset="https://raw.githubusercontent.com/simdjson/fastpysimdjson/main/doc/perf_ndjson_dark.png">
491
- <img src="https://raw.githubusercontent.com/simdjson/fastpysimdjson/main/doc/perf_ndjson.png" width="80%" alt="Reading NDJSON: fastsimdjson parse_many 0.94 GB/s, loads_many 0.38, msgspec decode_lines 0.37, fastsimdjson loads per line 0.33, msgspec decode per line 0.27, orjson per line 0.26, json per line 0.12">
531
+ <img src="https://raw.githubusercontent.com/simdjson/fastpysimdjson/main/doc/perf_ndjson.png" width="80%" alt="Reading NDJSON: fastsimdjson parse_many 0.94 GB/s, loads_many 0.38, msgspec decode_lines 0.37, fastsimdjson loads per line 0.33, msgspec decode per line 0.27, orjson per line 0.26, jiter per line 0.22, jiter without its string cache per line 0.20, json per line 0.12">
492
532
  </picture>
493
533
 
494
534
  | method | ms | GB/s |
495
535
  |---|---:|---:|
496
536
  | `json.loads` on each line | 169 | 0.12 |
537
+ | jiter on each line (no string cache) | 98 | 0.20 |
538
+ | jiter on each line (string cache) | 90 | 0.22 |
497
539
  | orjson on each line | 77 | 0.26 |
498
540
  | msgspec `decode` on each line | 74 | 0.27 |
499
541
  | fastsimdjson `loads` on each line | 60 | 0.33 |
@@ -10,7 +10,8 @@ function is faster still. It also reads streams of documents (NDJSON, JSON
10
10
  Lines), on many threads with free-threaded Python, and can extract only the
11
11
  fields you need from each document. It writes JSON in two ways: `dumps` returns the same `str` as
12
12
  `json.dumps`, and `dumpb` returns the same `bytes` as `orjson.dumps`, as fast
13
- as orjson.
13
+ as orjson; `dumpb_many` writes NDJSON, on many threads with free-threaded
14
+ Python.
14
15
 
15
16
  With pip:
16
17
 
@@ -126,6 +127,30 @@ serialize dataclasses, `datetime`, `date`, `time`, `UUID`, numpy arrays or
126
127
  `OPT_PASSTHROUGH_DATACLASS` and `OPT_PASSTHROUGH_DATETIME` were set, and the
127
128
  options that concern only these types are accepted and have no effect.
128
129
 
130
+ ### `dumpb_many`: NDJSON, on several threads
131
+
132
+ ```python
133
+ data = fastsimdjson.dumpb_many(records) # b'{...}\n{...}\n'
134
+ data = fastsimdjson.dumpb_many(records, threads=16) # free-threaded Python
135
+ ```
136
+
137
+ `dumpb_many(records, default=None, option=None, *, threads=None)` returns
138
+ the records (a list, a tuple or any iterable) as NDJSON `bytes`: each record
139
+ as `dumpb` writes it, followed by a newline, so that the output reads back
140
+ with `loads_many(data, format="lines")`. `default` and `option` are those of
141
+ `dumpb`, except `OPT_INDENT_2`, which would spread a record over several
142
+ lines (`ValueError`); `OPT_APPEND_NEWLINE` changes nothing.
143
+
144
+ On free-threaded Python, `threads=N` writes chunks of records on N threads at
145
+ once (`threads=0` or `None`: one per processor), with the worker threads of
146
+ `map_many`. The output and the errors are those of one thread: when records
147
+ cannot be serialized, the error of the first of them is raised (`default` may
148
+ then have been called on later records, in other threads, and it must be
149
+ safe to call from several threads at once). With the GIL, the calling thread
150
+ does all the work. Like `dumpb`, `dumpb_many` does not crash when other
151
+ threads change the records meanwhile, but the output then reflects some of
152
+ the changes and not others.
153
+
129
154
  ### Files
130
155
 
131
156
  ```python
@@ -242,7 +267,7 @@ calling thread. Views returned by `parse` remain valid.
242
267
  ## Build and test
243
268
 
244
269
  Python 3.10 or newer, and a C++17 compiler (clang, GCC or MSVC). The simdjson
245
- 5.0.2 and simdutf 9.2.1 amalgamations, and zmij 1.2 (shortest float
270
+ 5.0.7 and simdutf 9.2.1 amalgamations, and zmij 1.2 (shortest float
246
271
  formatting, MIT license), are already in `vendor/`.
247
272
 
248
273
  pip:
@@ -348,7 +373,7 @@ geometric mean over the 22 files (higher is better).
348
373
 
349
374
  <picture>
350
375
  <source media="(prefers-color-scheme: dark)" srcset="https://raw.githubusercontent.com/simdjson/fastpysimdjson/main/doc/perf_loads_dark.png">
351
- <img src="https://raw.githubusercontent.com/simdjson/fastpysimdjson/main/doc/perf_loads.png" width="80%" alt="Parsing JSON in Python: fastsimdjson loads 0.78 GB/s, orjson 0.60, msgspec 0.53, jiter 0.48, pysimdjson 0.44, cysimdjson 0.43, ujson 0.37, python-rapidjson 0.24, simplejson 0.23, json 0.22">
376
+ <img src="https://raw.githubusercontent.com/simdjson/fastpysimdjson/main/doc/perf_loads.png" width="80%" alt="Parsing JSON in Python: fastsimdjson loads 0.78 GB/s, orjson 0.60, msgspec 0.53, jiter 0.48, jiter without its string cache 0.44, pysimdjson 0.44, cysimdjson 0.43, ujson 0.37, python-rapidjson 0.24, simplejson 0.23, json 0.22">
352
377
  </picture>
353
378
 
354
379
  | parser | GB/s | vs `json.loads` |
@@ -359,7 +384,8 @@ geometric mean over the 22 files (higher is better).
359
384
  | ujson 6.0.0 | 0.37 | 1.66× |
360
385
  | cysimdjson 26.27 | 0.43 | 1.96× |
361
386
  | pysimdjson 7.0.2 | 0.44 | 1.98× |
362
- | jiter 0.17.0 | 0.48 | 2.21× |
387
+ | jiter 0.17.0 (no string cache) | 0.44 | 2.00× |
388
+ | jiter 0.17.0 (string cache, the default) | 0.48 | 2.21× |
363
389
  | msgspec 0.22.0 | 0.53 | 2.42× |
364
390
  | orjson 3.12.0 | 0.60 | 2.74× |
365
391
  | **fastsimdjson `loads`** | **0.78** | **3.53×** |
@@ -375,7 +401,7 @@ the collector enabled and 1.22× with it disabled (geometric means); without
375
401
  the collector, `canada.json`, `mesh.json` and `numbers.json` are ties. yyjson 4.0.6 is left out: it
376
402
  returns wrong strings for non-ASCII text. jiter (the parser of pydantic)
377
403
  caches strings by default; turning the cache off (`cache_mode=False`) makes it
378
- slower here (0.44 GB/s).
404
+ slower here.
379
405
 
380
406
  Parsing is no longer the bottleneck. simdjson alone parses these files at
381
407
  3.1 GB/s. It accounts for about a third of the time of `loads`; the rest goes
@@ -390,12 +416,14 @@ and the screen name of the 100 statuses of `twitter.json`:
390
416
 
391
417
  <picture>
392
418
  <source media="(prefers-color-scheme: dark)" srcset="https://raw.githubusercontent.com/simdjson/fastpysimdjson/main/doc/perf_parse_dark.png">
393
- <img src="https://raw.githubusercontent.com/simdjson/fastpysimdjson/main/doc/perf_parse.png" width="80%" alt="Reading part of twitter.json: fastsimdjson parse 158 µs, pysimdjson 179, cysimdjson 229, msgspec 336, fastsimdjson loads 861, orjson 1009, json 3922">
419
+ <img src="https://raw.githubusercontent.com/simdjson/fastpysimdjson/main/doc/perf_parse.png" width="80%" alt="Reading part of twitter.json: fastsimdjson parse 158 µs, pysimdjson 179, cysimdjson 229, msgspec 336, fastsimdjson loads 861, orjson 1009, jiter 1281, jiter without its string cache 1592, json 3922">
394
420
  </picture>
395
421
 
396
422
  | method | µs |
397
423
  |---|---:|
398
424
  | `json.loads` | 3922 |
425
+ | jiter (no string cache) | 1592 |
426
+ | jiter (string cache) | 1281 |
399
427
  | orjson | 1009 |
400
428
  | fastsimdjson `loads` | 861 |
401
429
  | msgspec (typed `Struct`) | 336 |
@@ -449,6 +477,18 @@ times faster than msgspec and 10 times faster than `json`. The comparison was
449
477
  run on a processor with AVX-512, which `dumpb` and orjson both use to escape
450
478
  strings; other x64 processors use SSE2 and ARM processors NEON.
451
479
 
480
+ ### Writing NDJSON with `dumpb_many`
481
+
482
+ The 593,945 events of two hours of [GH Archive](https://www.gharchive.org/)
483
+ (1.87 GB of NDJSON), written back as NDJSON from Python objects. With the
484
+ GIL (Python 3.14.6), one thread: `orjson.dumps` on each record, joined, runs
485
+ at 0.94 GB/s, `dumpb` on each record at 1.07 GB/s, and `dumpb_many` at
486
+ 1.41 GB/s. On free-threaded Python 3.14.6t, GB/s:
487
+
488
+ | threads | 1 | 8 | 32 | 64 |
489
+ |---|---:|---:|---:|---:|
490
+ | `dumpb_many(records, threads=N)` | 1.3 | 6.0 | 11 | 12 |
491
+
452
492
  ### Streams with `loads_many`
453
493
 
454
494
  20 MB of NDJSON (5268 objects and arrays, one per line), made from the same
@@ -456,12 +496,14 @@ files; best of three runs:
456
496
 
457
497
  <picture>
458
498
  <source media="(prefers-color-scheme: dark)" srcset="https://raw.githubusercontent.com/simdjson/fastpysimdjson/main/doc/perf_ndjson_dark.png">
459
- <img src="https://raw.githubusercontent.com/simdjson/fastpysimdjson/main/doc/perf_ndjson.png" width="80%" alt="Reading NDJSON: fastsimdjson parse_many 0.94 GB/s, loads_many 0.38, msgspec decode_lines 0.37, fastsimdjson loads per line 0.33, msgspec decode per line 0.27, orjson per line 0.26, json per line 0.12">
499
+ <img src="https://raw.githubusercontent.com/simdjson/fastpysimdjson/main/doc/perf_ndjson.png" width="80%" alt="Reading NDJSON: fastsimdjson parse_many 0.94 GB/s, loads_many 0.38, msgspec decode_lines 0.37, fastsimdjson loads per line 0.33, msgspec decode per line 0.27, orjson per line 0.26, jiter per line 0.22, jiter without its string cache per line 0.20, json per line 0.12">
460
500
  </picture>
461
501
 
462
502
  | method | ms | GB/s |
463
503
  |---|---:|---:|
464
504
  | `json.loads` on each line | 169 | 0.12 |
505
+ | jiter on each line (no string cache) | 98 | 0.20 |
506
+ | jiter on each line (string cache) | 90 | 0.22 |
465
507
  | orjson on each line | 77 | 0.26 |
466
508
  | msgspec `decode` on each line | 74 | 0.27 |
467
509
  | fastsimdjson `loads` on each line | 60 | 0.33 |
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "fastsimdjson"
7
- version = "0.7.0"
7
+ version = "0.9.0"
8
8
  description = "Fast JSON parsing for Python, built on simdjson"
9
9
  readme = "README.md"
10
10
  authors = [