Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Parse report_json files with serde #18

Merged
merged 1 commit into from
Aug 30, 2024
Merged

Conversation

Swatinem
Copy link
Collaborator

@Swatinem Swatinem commented Jul 31, 2024

Replaces the hand-written winnow-based parser with a bunch of struct definitions along with deriving serde::Deserialize.


The Serde definitions look very simple, and parsing performance seems to be 8-20x faster than the hand built parser, and doing a lot less less intermediate allocations.

This is a previous comparison:

pyreport              fastest       │ slowest       │ median        │ mean          │ samples │ iters
├─ serde                            │               │               │               │         │
│  ├─ complex_report  19.47 ms      │ 25.82 ms      │ 21.48 ms      │ 21.66 ms      │ 23      │ 23
│  │                  alloc:        │               │               │               │         │
│  │                    1629        │ 0             │ 1629          │ 1558          │         │
│  │                    401.6 KB    │ 0 B           │ 401.6 KB      │ 384.2 KB      │         │
│  │                  dealloc:      │               │               │               │         │
│  │                    14          │ 0             │ 14            │ 13.39         │         │
│  │                    91.82 KB    │ 0 B           │ 91.82 KB      │ 87.83 KB      │         │
│  ╰─ simple_report   2.017 µs      │ 155.4 µs      │ 2.12 µs       │ 2.4 µs        │ 182288  │ 182288
│                     alloc:        │               │               │               │         │
│                       6           │ 6             │ 6             │ 6             │         │
│                       4.056 KB    │ 4.056 KB      │ 4.056 KB      │ 4.056 KB      │         │
│                     dealloc:      │               │               │               │         │
│                       6           │ 6             │ 6             │ 6             │         │
│                       4.056 KB    │ 4.056 KB      │ 4.056 KB      │ 4.056 KB      │         │
╰─ winnow                           │               │               │               │         │
   ├─ complex_report  471 ms        │ 516.1 ms      │ 497.7 ms      │ 495.7 ms      │ 10      │ 10
   │                  alloc:        │               │               │               │         │
   │                    1020786     │ 1020786       │ 1020786       │ 1020786       │         │
   │                    81.6 MB     │ 81.6 MB       │ 81.6 MB       │ 81.6 MB       │         │
   │                  dealloc:      │               │               │               │         │
   │                    1020784     │ 1020784       │ 1020784       │ 1020784       │         │
   │                    122.4 MB    │ 122.4 MB      │ 122.4 MB      │ 122.4 MB      │         │
   │                  grow:         │               │               │               │         │
   │                    322539      │ 322539        │ 322539        │ 322539        │         │
   │                    40.89 MB    │ 40.89 MB      │ 40.89 MB      │ 40.89 MB      │         │
   ╰─ simple_report   14.31 µs      │ 168.7 µs      │ 16.18 µs      │ 17.38 µs      │ 28158   │ 28158
                      alloc:        │               │               │               │         │
                        51          │ 51            │ 51            │ 51            │         │
                        4.613 KB    │ 4.613 KB      │ 4.613 KB      │ 4.613 KB      │         │
                      dealloc:      │               │               │               │         │
                        51          │ 51            │ 51            │ 51            │         │
                        4.725 KB    │ 4.725 KB      │ 4.725 KB      │ 4.725 KB      │         │
                      grow:         │               │               │               │         │
                        12          │ 12            │ 12            │ 12            │         │
                        112 B       │ 112 B         │ 112 B         │ 112 B         │         │

@Swatinem Swatinem self-assigned this Jul 31, 2024
Copy link

codecov bot commented Jul 31, 2024

Codecov Report

All modified and coverable lines are covered by tests ✅

Project coverage is 98.62%. Comparing base (1b43a89) to head (05f2083).
Report is 1 commits behind head on main.

✅ All tests successful. No failed tests found.

Additional details and impacted files
@@            Coverage Diff             @@
##             main      #18      +/-   ##
==========================================
- Coverage   98.69%   98.62%   -0.07%     
==========================================
  Files          20       20              
  Lines        7333     6962     -371     
==========================================
- Hits         7237     6866     -371     
  Misses         96       96              
Components Coverage Δ
core 98.61% <100.00%> (-0.08%) ⬇️
bindings 100.00% <ø> (ø)
python 100.00% <ø> (ø)

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

@Swatinem Swatinem force-pushed the swatinem/bench-parsing branch from 04024f7 to 7afa7eb Compare August 1, 2024 07:17
Base automatically changed from swatinem/bench-parsing to main August 1, 2024 07:45
@Swatinem Swatinem force-pushed the swatinem/parse-report-serde branch from 1a6bd5f to 331ce5a Compare August 1, 2024 11:16
@Swatinem
Copy link
Collaborator Author

Swatinem commented Aug 1, 2024

I updated this a little bit, adding divans AllocProfiler. The results are in the updated PR description.

I’m a bit surprised myself by the results. Looking more in-depth into how the report_json file actually looks like, it turns out the bulk of the file size comes from the session_totals (aka file.XXX.3).

In particular, those totals are parsed in full fidelity, but thrown away and never used, as all the totals are calculated on-demand instead:

let (filename, file_summary) = delimited(ws, parse_kv, ws).parse_next(buf)?;
let Some(chunks_index) = file_summary
.get(0)

The serde code is using IgnoredAny to just skip over all of that.


Related to memory usage optimizations, I have used by very own smol_buf::Str24, which is using small string optimizations.

The memory usage is effectively:

  • 24 + 8 = 32 bytes per File entry, plus HashMap overhead
  • at least 8 + 288 = 296 bytes per Session entry, plus long non-inline Strings, plus HashMap overhead

We can go lower for Session still by using Str16 rather than Str24 for all the strings that tend to be short.

@Swatinem Swatinem force-pushed the swatinem/parse-report-serde branch 2 times, most recently from a5eae16 to ae71dd1 Compare August 27, 2024 10:00
@Swatinem Swatinem marked this pull request as ready for review August 27, 2024 10:00
@Swatinem Swatinem requested a review from matt-codecov August 27, 2024 10:01
Copy link
Collaborator

@matt-codecov matt-codecov left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

thanks for taking care of this! now that python doesn't emit the session_totals bloat anymore and the p99 report json size is stable around 8mb we don't need to be paranoid about fitting everything in memory

love the perf win and the big red diff

src/parsers/pyreport/report_json.rs Outdated Show resolved Hide resolved
@matt-codecov
Copy link
Collaborator

if i had noticed this was ready for review i'd have let you merge it before merging #27, sorry!

Replaces the hand-written `winnow`-based parser with a bunch of struct definitions along with deriving `serde::Deserialize`.
@Swatinem Swatinem force-pushed the swatinem/parse-report-serde branch from ae71dd1 to 05f2083 Compare August 30, 2024 08:40
@Swatinem Swatinem merged commit 5f3091a into main Aug 30, 2024
8 checks passed
@Swatinem Swatinem deleted the swatinem/parse-report-serde branch August 30, 2024 09:11
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants