fastsimdjson 0.5.0__tar.gz → 0.7.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.5.0/src/fastsimdjson.egg-info → fastsimdjson-0.7.0}/PKG-INFO +55 -17
  2. {fastsimdjson-0.5.0 → fastsimdjson-0.7.0}/README.md +53 -16
  3. {fastsimdjson-0.5.0 → fastsimdjson-0.7.0}/pyproject.toml +2 -2
  4. {fastsimdjson-0.5.0 → fastsimdjson-0.7.0}/src/fastsimdjson.cpp +606 -58
  5. {fastsimdjson-0.5.0 → fastsimdjson-0.7.0/src/fastsimdjson.egg-info}/PKG-INFO +55 -17
  6. {fastsimdjson-0.5.0 → fastsimdjson-0.7.0}/src/fastsimdjson.egg-info/requires.txt +1 -0
  7. {fastsimdjson-0.5.0 → fastsimdjson-0.7.0}/tests/test_stream.py +194 -0
  8. {fastsimdjson-0.5.0 → fastsimdjson-0.7.0}/MANIFEST.in +0 -0
  9. {fastsimdjson-0.5.0 → fastsimdjson-0.7.0}/setup.cfg +0 -0
  10. {fastsimdjson-0.5.0 → fastsimdjson-0.7.0}/setup.py +0 -0
  11. {fastsimdjson-0.5.0 → fastsimdjson-0.7.0}/src/fastsimdjson.egg-info/SOURCES.txt +0 -0
  12. {fastsimdjson-0.5.0 → fastsimdjson-0.7.0}/src/fastsimdjson.egg-info/dependency_links.txt +0 -0
  13. {fastsimdjson-0.5.0 → fastsimdjson-0.7.0}/src/fastsimdjson.egg-info/top_level.txt +0 -0
  14. {fastsimdjson-0.5.0 → fastsimdjson-0.7.0}/tests/test_dumps.py +0 -0
  15. {fastsimdjson-0.5.0 → fastsimdjson-0.7.0}/tests/test_files.py +0 -0
  16. {fastsimdjson-0.5.0 → fastsimdjson-0.7.0}/tests/test_lazy.py +0 -0
  17. {fastsimdjson-0.5.0 → fastsimdjson-0.7.0}/tests/test_loads.py +0 -0
  18. {fastsimdjson-0.5.0 → fastsimdjson-0.7.0}/vendor/simdjson.cpp +0 -0
  19. {fastsimdjson-0.5.0 → fastsimdjson-0.7.0}/vendor/simdjson.h +0 -0
  20. {fastsimdjson-0.5.0 → fastsimdjson-0.7.0}/vendor/simdutf.cpp +0 -0
  21. {fastsimdjson-0.5.0 → fastsimdjson-0.7.0}/vendor/simdutf.h +0 -0
  22. {fastsimdjson-0.5.0 → fastsimdjson-0.7.0}/vendor/simdutf_c.h +0 -0
  23. {fastsimdjson-0.5.0 → fastsimdjson-0.7.0}/vendor/zmij-LICENSE +0 -0
  24. {fastsimdjson-0.5.0 → fastsimdjson-0.7.0}/vendor/zmij.cc +0 -0
  25. {fastsimdjson-0.5.0 → fastsimdjson-0.7.0}/vendor/zmij.h +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: fastsimdjson
3
- Version: 0.5.0
3
+ Version: 0.7.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
@@ -25,6 +25,7 @@ Provides-Extra: test
25
25
  Requires-Dist: pytest; extra == "test"
26
26
  Provides-Extra: bench
27
27
  Requires-Dist: orjson; extra == "bench"
28
+ Requires-Dist: jiter; extra == "bench"
28
29
  Requires-Dist: msgspec; extra == "bench"
29
30
  Requires-Dist: pysimdjson; extra == "bench"
30
31
  Requires-Dist: cysimdjson; extra == "bench"
@@ -38,7 +39,8 @@ replacement for the `json` module's `loads`, `load`, `dumps` and `dump`, and
38
39
  it can be over 3 times faster than the standard `json.loads` and
39
40
  `json.dumps`. When you only need part of a document, its lazy `parse`
40
41
  function is faster still. It also reads streams of documents (NDJSON, JSON
41
- Lines). It writes JSON in two ways: `dumps` returns the same `str` as
42
+ Lines), on many threads with free-threaded Python, and can extract only the
43
+ fields you need from each document. It writes JSON in two ways: `dumps` returns the same `str` as
42
44
  `json.dumps`, and `dumpb` returns the same `bytes` as `orjson.dumps`, as fast
43
45
  as orjson.
44
46
 
@@ -225,12 +227,45 @@ threads. An exception raised by `fn` is raised again, for the first document
225
227
  where it happened; `fn` may then have been called on later documents, in
226
228
  other threads, and it must be safe to call from several threads at once.
227
229
 
228
- `map_many` scales best: each document is built, used and freed in the same
229
- thread, and only `fn`'s results come back. `loads_many` must return every
230
+ The worker threads are started by the first call that needs them and kept
231
+ for later calls (they wait, detached from the interpreter, between calls,
232
+ and stop at exit). `map_many` scales best: each document is built, used and
233
+ freed in the same thread, and only `fn`'s results come back. `loads_many` must return every
230
234
  document, and the Python objects of all of them, to the calling thread.
231
235
  With the GIL (the default build of Python), the calling thread does all the
232
236
  work: `threads` has no effect on speed.
233
237
 
238
+ ### Only some fields: `fields=`
239
+
240
+ When you need a few values from each document, `fields` extracts them
241
+ without building the documents: only the values found become Python
242
+ objects.
243
+
244
+ ```python
245
+ events = fastsimdjson.loads_many(data, fields=["/type", "/payload/action"])
246
+ # (type, action) for each document
247
+
248
+ def count(t):
249
+ kind, action, language = t
250
+ ...
251
+ fastsimdjson.map_many(count, data, threads=64,
252
+ fields=["/type", "/payload/action",
253
+ "/payload/pull_request/base/repo/language"])
254
+ ```
255
+
256
+ `loads_many` and `map_many` accept `fields`: a JSON Pointer (RFC 6901), or a
257
+ list or tuple of them. Each document then becomes the value at the pointer,
258
+ or a tuple of the values at the pointers, in order. A value that is an
259
+ object or an array is built in full. A pointer that leads nowhere (a
260
+ missing key, an index past the end, or a step into a scalar) gives
261
+ `default`, `None` unless you pass another value; a JSON `null` gives `None`.
262
+ As with `at_pointer`, a key that appears twice in an object refers to its
263
+ first occurrence. `fields` cannot be combined with `lazy=True`.
264
+
265
+ On 7.6 GB of GitHub events, `map_many` with three fields ran at 2.8 GB/s on
266
+ one core of an Intel Xeon Gold 6548N, against 2.2 GB/s with lazy views
267
+ (`lazy=True`). With 64 threads, it ran at 78 GB/s (lazy views: 61 GB/s).
268
+
234
269
  ### `release`
235
270
 
236
271
  `release()` frees the simdjson parser and the string caches kept by the
@@ -305,8 +340,8 @@ The suite builds an ~80 MB document and a list of 16,777,221 integers, so
305
340
  give it some RAM.
306
341
 
307
342
  The benchmark scripts need simdjson-data and the `bench` extra (orjson,
308
- msgspec, pysimdjson, cysimdjson). `bench.py` times `loads` against
309
- `json.loads` and orjson, `bench_lazy.py` times `parse` against pysimdjson and
343
+ jiter, msgspec, pysimdjson, cysimdjson). `bench.py` times `loads` against
344
+ `json.loads`, orjson and jiter, `bench_lazy.py` times `parse` against pysimdjson and
310
345
  cysimdjson, `bench_dumps.py` times `dumps` against `json.dumps` and `dumpb`
311
346
  against orjson and msgspec, and `bench_many.py` times `loads_many`.
312
347
 
@@ -345,7 +380,7 @@ geometric mean over the 22 files (higher is better).
345
380
 
346
381
  <picture>
347
382
  <source media="(prefers-color-scheme: dark)" srcset="https://raw.githubusercontent.com/simdjson/fastpysimdjson/main/doc/perf_loads_dark.png">
348
- <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, pysimdjson 0.44, cysimdjson 0.43, ujson 0.37, python-rapidjson 0.24, simplejson 0.23, json 0.22">
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">
349
384
  </picture>
350
385
 
351
386
  | parser | GB/s | vs `json.loads` |
@@ -356,6 +391,7 @@ geometric mean over the 22 files (higher is better).
356
391
  | ujson 6.0.0 | 0.37 | 1.66× |
357
392
  | cysimdjson 26.27 | 0.43 | 1.96× |
358
393
  | pysimdjson 7.0.2 | 0.44 | 1.98× |
394
+ | jiter 0.17.0 | 0.48 | 2.21× |
359
395
  | msgspec 0.22.0 | 0.53 | 2.42× |
360
396
  | orjson 3.12.0 | 0.60 | 2.74× |
361
397
  | **fastsimdjson `loads`** | **0.78** | **3.53×** |
@@ -369,7 +405,9 @@ gain comes from pausing the garbage collector while the objects are built.
369
405
  Timing each call on its own, fastsimdjson's lead over orjson is 1.33× with
370
406
  the collector enabled and 1.22× with it disabled (geometric means); without
371
407
  the collector, `canada.json`, `mesh.json` and `numbers.json` are ties. yyjson 4.0.6 is left out: it
372
- returns wrong strings for non-ASCII text.
408
+ returns wrong strings for non-ASCII text. jiter (the parser of pydantic)
409
+ caches strings by default; turning the cache off (`cache_mode=False`) makes it
410
+ slower here (0.44 GB/s).
373
411
 
374
412
  Parsing is no longer the bottleneck. simdjson alone parses these files at
375
413
  3.1 GB/s. It accounts for about a third of the time of `loads`; the rest goes
@@ -475,15 +513,15 @@ disabled (see below), Intel Xeon Gold 6548N (64 cores); GB/s:
475
513
 
476
514
  | threads | 1 | 2 | 4 | 8 | 16 | 32 | 64 |
477
515
  |---|---:|---:|---:|---:|---:|---:|---:|
478
- | `map_many(len, data, threads=N)` | 0.91 | 1.77 | 3.28 | 6.22 | 10.9 | 15.4 | **21.2** |
479
- | `parse_many(data, threads=N)` | 1.82 | 3.06 | 4.80 | 6.90 | 10.2 | 12.3 | 16.4 |
480
- | `loads_many(data, threads=N)` | 0.82 | 1.43 | 2.26 | 3.88 | 5.77 | 7.45 | 8.63 |
481
-
482
- `map_many` is 23 times faster on 64 threads than on one. `loads_many` builds
483
- every document for the calling thread and is 11 times faster. With the
484
- collector enabled, Python collects after the call returns: it examines the
485
- millions of objects just created, which takes about 0.1 s here, with or
486
- without threads.
516
+ | `map_many(len, data, threads=N)` | 1.03 | 1.78 | 3.66 | 6.82 | 13.0 | 23.7 | **28.8** |
517
+ | `parse_many(data, threads=N)` | 2.00 | 2.84 | 5.28 | 9.26 | 16.1 | 22.7 | 25.8 |
518
+ | `loads_many(data, threads=N)` | 0.90 | 1.44 | 2.79 | 4.99 | 8.98 | 13.3 | 15.4 |
519
+
520
+ `map_many` is 28 times faster on 64 threads than on one (up to 38 GB/s when
521
+ nothing else runs in the process). `loads_many` builds every document for
522
+ the calling thread and is 17 times faster. With the collector enabled,
523
+ Python collects after the call returns: it examines the millions of objects
524
+ just created, which takes about 0.1 s here, with or without threads.
487
525
 
488
526
  ## Limitations
489
527
 
@@ -7,7 +7,8 @@ replacement for the `json` module's `loads`, `load`, `dumps` and `dump`, and
7
7
  it can be over 3 times faster than the standard `json.loads` and
8
8
  `json.dumps`. When you only need part of a document, its lazy `parse`
9
9
  function is faster still. It also reads streams of documents (NDJSON, JSON
10
- Lines). It writes JSON in two ways: `dumps` returns the same `str` as
10
+ Lines), on many threads with free-threaded Python, and can extract only the
11
+ fields you need from each document. It writes JSON in two ways: `dumps` returns the same `str` as
11
12
  `json.dumps`, and `dumpb` returns the same `bytes` as `orjson.dumps`, as fast
12
13
  as orjson.
13
14
 
@@ -194,12 +195,45 @@ threads. An exception raised by `fn` is raised again, for the first document
194
195
  where it happened; `fn` may then have been called on later documents, in
195
196
  other threads, and it must be safe to call from several threads at once.
196
197
 
197
- `map_many` scales best: each document is built, used and freed in the same
198
- thread, and only `fn`'s results come back. `loads_many` must return every
198
+ The worker threads are started by the first call that needs them and kept
199
+ for later calls (they wait, detached from the interpreter, between calls,
200
+ and stop at exit). `map_many` scales best: each document is built, used and
201
+ freed in the same thread, and only `fn`'s results come back. `loads_many` must return every
199
202
  document, and the Python objects of all of them, to the calling thread.
200
203
  With the GIL (the default build of Python), the calling thread does all the
201
204
  work: `threads` has no effect on speed.
202
205
 
206
+ ### Only some fields: `fields=`
207
+
208
+ When you need a few values from each document, `fields` extracts them
209
+ without building the documents: only the values found become Python
210
+ objects.
211
+
212
+ ```python
213
+ events = fastsimdjson.loads_many(data, fields=["/type", "/payload/action"])
214
+ # (type, action) for each document
215
+
216
+ def count(t):
217
+ kind, action, language = t
218
+ ...
219
+ fastsimdjson.map_many(count, data, threads=64,
220
+ fields=["/type", "/payload/action",
221
+ "/payload/pull_request/base/repo/language"])
222
+ ```
223
+
224
+ `loads_many` and `map_many` accept `fields`: a JSON Pointer (RFC 6901), or a
225
+ list or tuple of them. Each document then becomes the value at the pointer,
226
+ or a tuple of the values at the pointers, in order. A value that is an
227
+ object or an array is built in full. A pointer that leads nowhere (a
228
+ missing key, an index past the end, or a step into a scalar) gives
229
+ `default`, `None` unless you pass another value; a JSON `null` gives `None`.
230
+ As with `at_pointer`, a key that appears twice in an object refers to its
231
+ first occurrence. `fields` cannot be combined with `lazy=True`.
232
+
233
+ On 7.6 GB of GitHub events, `map_many` with three fields ran at 2.8 GB/s on
234
+ one core of an Intel Xeon Gold 6548N, against 2.2 GB/s with lazy views
235
+ (`lazy=True`). With 64 threads, it ran at 78 GB/s (lazy views: 61 GB/s).
236
+
203
237
  ### `release`
204
238
 
205
239
  `release()` frees the simdjson parser and the string caches kept by the
@@ -274,8 +308,8 @@ The suite builds an ~80 MB document and a list of 16,777,221 integers, so
274
308
  give it some RAM.
275
309
 
276
310
  The benchmark scripts need simdjson-data and the `bench` extra (orjson,
277
- msgspec, pysimdjson, cysimdjson). `bench.py` times `loads` against
278
- `json.loads` and orjson, `bench_lazy.py` times `parse` against pysimdjson and
311
+ jiter, msgspec, pysimdjson, cysimdjson). `bench.py` times `loads` against
312
+ `json.loads`, orjson and jiter, `bench_lazy.py` times `parse` against pysimdjson and
279
313
  cysimdjson, `bench_dumps.py` times `dumps` against `json.dumps` and `dumpb`
280
314
  against orjson and msgspec, and `bench_many.py` times `loads_many`.
281
315
 
@@ -314,7 +348,7 @@ geometric mean over the 22 files (higher is better).
314
348
 
315
349
  <picture>
316
350
  <source media="(prefers-color-scheme: dark)" srcset="https://raw.githubusercontent.com/simdjson/fastpysimdjson/main/doc/perf_loads_dark.png">
317
- <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, pysimdjson 0.44, cysimdjson 0.43, ujson 0.37, python-rapidjson 0.24, simplejson 0.23, json 0.22">
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">
318
352
  </picture>
319
353
 
320
354
  | parser | GB/s | vs `json.loads` |
@@ -325,6 +359,7 @@ geometric mean over the 22 files (higher is better).
325
359
  | ujson 6.0.0 | 0.37 | 1.66× |
326
360
  | cysimdjson 26.27 | 0.43 | 1.96× |
327
361
  | pysimdjson 7.0.2 | 0.44 | 1.98× |
362
+ | jiter 0.17.0 | 0.48 | 2.21× |
328
363
  | msgspec 0.22.0 | 0.53 | 2.42× |
329
364
  | orjson 3.12.0 | 0.60 | 2.74× |
330
365
  | **fastsimdjson `loads`** | **0.78** | **3.53×** |
@@ -338,7 +373,9 @@ gain comes from pausing the garbage collector while the objects are built.
338
373
  Timing each call on its own, fastsimdjson's lead over orjson is 1.33× with
339
374
  the collector enabled and 1.22× with it disabled (geometric means); without
340
375
  the collector, `canada.json`, `mesh.json` and `numbers.json` are ties. yyjson 4.0.6 is left out: it
341
- returns wrong strings for non-ASCII text.
376
+ returns wrong strings for non-ASCII text. jiter (the parser of pydantic)
377
+ caches strings by default; turning the cache off (`cache_mode=False`) makes it
378
+ slower here (0.44 GB/s).
342
379
 
343
380
  Parsing is no longer the bottleneck. simdjson alone parses these files at
344
381
  3.1 GB/s. It accounts for about a third of the time of `loads`; the rest goes
@@ -444,15 +481,15 @@ disabled (see below), Intel Xeon Gold 6548N (64 cores); GB/s:
444
481
 
445
482
  | threads | 1 | 2 | 4 | 8 | 16 | 32 | 64 |
446
483
  |---|---:|---:|---:|---:|---:|---:|---:|
447
- | `map_many(len, data, threads=N)` | 0.91 | 1.77 | 3.28 | 6.22 | 10.9 | 15.4 | **21.2** |
448
- | `parse_many(data, threads=N)` | 1.82 | 3.06 | 4.80 | 6.90 | 10.2 | 12.3 | 16.4 |
449
- | `loads_many(data, threads=N)` | 0.82 | 1.43 | 2.26 | 3.88 | 5.77 | 7.45 | 8.63 |
450
-
451
- `map_many` is 23 times faster on 64 threads than on one. `loads_many` builds
452
- every document for the calling thread and is 11 times faster. With the
453
- collector enabled, Python collects after the call returns: it examines the
454
- millions of objects just created, which takes about 0.1 s here, with or
455
- without threads.
484
+ | `map_many(len, data, threads=N)` | 1.03 | 1.78 | 3.66 | 6.82 | 13.0 | 23.7 | **28.8** |
485
+ | `parse_many(data, threads=N)` | 2.00 | 2.84 | 5.28 | 9.26 | 16.1 | 22.7 | 25.8 |
486
+ | `loads_many(data, threads=N)` | 0.90 | 1.44 | 2.79 | 4.99 | 8.98 | 13.3 | 15.4 |
487
+
488
+ `map_many` is 28 times faster on 64 threads than on one (up to 38 GB/s when
489
+ nothing else runs in the process). `loads_many` builds every document for
490
+ the calling thread and is 17 times faster. With the collector enabled,
491
+ Python collects after the call returns: it examines the millions of objects
492
+ just created, which takes about 0.1 s here, with or without threads.
456
493
 
457
494
  ## Limitations
458
495
 
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "fastsimdjson"
7
- version = "0.5.0"
7
+ version = "0.7.0"
8
8
  description = "Fast JSON parsing for Python, built on simdjson"
9
9
  readme = "README.md"
10
10
  authors = [
@@ -34,7 +34,7 @@ Issues = "https://github.com/simdjson/fastpysimdjson/issues"
34
34
 
35
35
  [project.optional-dependencies]
36
36
  test = ["pytest"]
37
- bench = ["orjson", "msgspec", "pysimdjson", "cysimdjson"]
37
+ bench = ["orjson", "jiter", "msgspec", "pysimdjson", "cysimdjson"]
38
38
 
39
39
  [tool.cibuildwheel]
40
40
  skip = ["pp*"]