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

Large performance regression in some popular CSV parsing libs (Canary vs 1.2.2) #17373

Open
leeoniya opened this issue Feb 16, 2025 · 5 comments
Labels
bug Something isn't working needs triage

Comments

@leeoniya
Copy link

leeoniya commented Feb 16, 2025

What version of Bun is running?

1.2.3-canary.94+3711280d4

What platform is your computer?

Linux 6.13.2-arch1-1 x86_64 unknown

What steps can reproduce the bug?

  1. grab this CSV: https://github.com/Schlumberger/hackathon/blob/master/backend/dataset/data-large.csv
  2. load it into memory: const csvStr = fs.readFileSync('data-large.csv', 'utf8');
  3. use papaparse to parse it: let rows = Papa.parse(csvStr).data;
  4. use convert-csv-to-json to parse it: let rows = convertCSVtoJSON.fieldDelimiter(',').supportQuotedField(true).csvStringToJson(csvStr);

What is the expected behavior?

unchanged (or better) performance for papaparse and convert-csv-to-json

What do you see instead?

much worse performance

Additional information

i update Bun canary builds very often and only noticed this recently. i suspect it was the Webkit upgrade in #17095 (cc @190n @Jarred-Sumner)

Bun 1.2.2

  ┌───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐
  │ data-large2.csv (36 MB, 37 cols x 130K rows)                                                                                  │
  ├─────────────────────┬────────┬─────────────────────────────────────────────────────────────┬──────────────────────────────────┤
  │ Name                │ Rows/s │ Throughput (MiB/s)                                          │ RSS above 84 MiB baseline (MiB)  │
  ├─────────────────────┼────────┼─────────────────────────────────────────────────────────────┼──────────────────────────────────┤
  │ uDSV                │ 899K   │ ░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░ 249 │ ░░░░░░░░░░░░░░░ 169              │
=>│ PapaParse           │ 876K   │ ░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░ 243  │ ░░░░░░░░░░░░░░░░░░ 209           │
  │ d3-dsv              │ 824K   │ ░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░ 228     │ ░░░░░░░░░░░░░░░░░░░░ 224         │
  │ csv-simple-parser   │ 761K   │ ░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░ 211         │ ░░░░░░░░░░░░░░░ 169              │
  │ tiddlycsv           │ 523K   │ ░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░ 145                        │ ░░░░░░░░░░░░░░░░░░░░░░░░ 271     │
=>│ CSVtoJSON           │ 503K   │ ░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░ 139                         │ ░░░░░░░░░░░░░░ 162               │
  │ but-csv             │ 473K   │ ░░░░░░░░░░░░░░░░░░░░░░░░░░░░░ 131                           │ ░░░░░░░░░░░░░░░░░░░░░░░░░░░░ 326 │
  │ csv-rex             │ 470K   │ ░░░░░░░░░░░░░░░░░░░░░░░░░░░░░ 130                           │ ░░░░░░░░░░░░░ 150                │
  │ arquero (native)    │ 470K   │ ░░░░░░░░░░░░░░░░░░░░░░░░░░░░░ 130                           │ ░░░░░░░░░░░░░░░░░░░░░░ 253       │
  │ csv42               │ 458K   │ ░░░░░░░░░░░░░░░░░░░░░░░░░░░░ 127                            │ ░░░░░░░░░░░░░░░ 168              │
  │ achilles-csv-parser │ 407K   │ ░░░░░░░░░░░░░░░░░░░░░░░░░ 113                               │ ░░░░░░░░░░░░░░░░ 178             │
  │ ACsv                │ 322K   │ ░░░░░░░░░░░░░░░░░░░░ 89.4                                   │ ░░░░░░░░░░░░░░░░░░░░░ 239        │
  └─────────────────────┴────────┴─────────────────────────────────────────────────────────────┴──────────────────────────────────┘

Bun 1.2.3-canary.94+3711280d4

  ┌───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐
  │ data-large2.csv (36 MB, 37 cols x 130K rows)                                                                                  │
  ├─────────────────────┬────────┬─────────────────────────────────────────────────────────────┬──────────────────────────────────┤
  │ Name                │ Rows/s │ Throughput (MiB/s)                                          │ RSS above 82 MiB baseline (MiB)  │
  ├─────────────────────┼────────┼─────────────────────────────────────────────────────────────┼──────────────────────────────────┤
  │ uDSV                │ 1.01M  │ ░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░ 279 │ ░░░░░░░░░░░░░░░ 168              │
  │ d3-dsv              │ 906K   │ ░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░ 251      │ ░░░░░░░░░░░░░░░░░░░░ 223         │
  │ csv-simple-parser   │ 784K   │ ░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░ 217             │ ░░░░░░░░░░░░░░░ 167              │
  │ but-csv             │ 547K   │ ░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░ 152                          │ ░░░░░░░░░░░░░░░░░░░░░░░░░░░░ 326 │
  │ tiddlycsv           │ 544K   │ ░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░ 151                          │ ░░░░░░░░░░░░░░░░░░░░░░░░ 271     │
  │ csv42               │ 504K   │ ░░░░░░░░░░░░░░░░░░░░░░░░░░░░ 140                            │ ░░░░░░░░░░░░░░░░░ 197            │
  │ arquero (native)    │ 500K   │ ░░░░░░░░░░░░░░░░░░░░░░░░░░░░ 139                            │ ░░░░░░░░░░░░░░░░░░░░░░ 251       │
  │ csv-rex             │ 483K   │ ░░░░░░░░░░░░░░░░░░░░░░░░░░░ 134                             │ ░░░░░░░░░░░░░░░░░░░░░ 241        │
  │ achilles-csv-parser │ 425K   │ ░░░░░░░░░░░░░░░░░░░░░░░░ 118                                │ ░░░░░░░░░░░░░░░░ 178             │
  │ ACsv                │ 318K   │ ░░░░░░░░░░░░░░░░░░ 88.2                                     │ ░░░░░░░░░░░░░░░░░░░░░ 240        │
=>│ PapaParse           │ 261K   │ ░░░░░░░░░░░░░░░ 72.3                                        │ ░░░░░░░░░░░░ 137                 │
=>│ CSVtoJSON           │ 205K   │ ░░░░░░░░░░░░ 56.8                                           │ ░░░░░░░ 72                       │
  └─────────────────────┴────────┴─────────────────────────────────────────────────────────────┴──────────────────────────────────┘
Node v23.8.0

  ┌───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐
  │ data-large2.csv (36 MB, 37 cols x 130K rows)                                                                                  │
  ├─────────────────────┬────────┬─────────────────────────────────────────────────────────────┬──────────────────────────────────┤
  │ Name                │ Rows/s │ Throughput (MiB/s)                                          │ RSS above 85 MiB baseline (MiB)  │
  ├─────────────────────┼────────┼─────────────────────────────────────────────────────────────┼──────────────────────────────────┤
  │ uDSV                │ 597K   │ ░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░ 165 │ ░░░░░░░░░░░░░░░░ 182             │
  │ csv-simple-parser   │ 563K   │ ░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░ 156    │ ░░░░░░░░░░░░░░░░ 181             │
  │ ACsv                │ 442K   │ ░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░ 123               │ ░░░░░░░░░░░░░░░░ 181             │
=>│ PapaParse           │ 442K   │ ░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░ 123               │ ░░░░░░░░░░░░░░░░ 182             │
  │ d3-dsv              │ 441K   │ ░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░ 122               │ ░░░░░░░░░░░░░░░░ 180             │
  │ but-csv             │ 428K   │ ░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░ 119                │ ░░░░░░░░░░░░░░░ 172              │
  │ tiddlycsv           │ 416K   │ ░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░ 115                 │ ░░░░░░░░░░░░░░░░ 180             │
  │ csv-rex             │ 364K   │ ░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░ 101                      │ ░░░░░░░░░░░░░░░ 176              │
  │ arquero (native)    │ 254K   │ ░░░░░░░░░░░░░░░░░░░░░░░░ 70.5                               │ ░░░░░░░░░░░░░░░░░░░░░░░░░░ 301   │
  │ csv42               │ 223K   │ ░░░░░░░░░░░░░░░░░░░░░ 61.8                                  │ ░░░░░░░░░░░░░░░░░░░░░░░░░░░░ 335 │
  │ achilles-csv-parser │ 207K   │ ░░░░░░░░░░░░░░░░░░░░ 57.4                                   │ ░░░░░░░░░░░░░░░░░░░░░░░░░░░░ 333 │
=>│ CSVtoJSON           │ 183K   │ ░░░░░░░░░░░░░░░░░ 50.6                                      │ ░░░░░░░░░░░░░░░░░░░░░░░░░░░░ 327 │
  └─────────────────────┴────────┴─────────────────────────────────────────────────────────────┴──────────────────────────────────┘
@leeoniya leeoniya added bug Something isn't working needs triage labels Feb 16, 2025
@Jarred-Sumner
Copy link
Collaborator

Ooh I wonder if it’s using Buffer

@leeoniya
Copy link
Author

@Jarred-Sumner
Copy link
Collaborator

It is using node streams though, which internally uses Buffer a lot of the time.

@Jarred-Sumner
Copy link
Collaborator

My guess without running a profiler is it’s either regex changes or something related to buffer

@leeoniya
Copy link
Author

this csv is quoteless so probably takes the fastMode path that doesnt seem to do any stream or buffer related things.

https://github.com/mholt/PapaParse/blob/e3c7b2628c68b868fd09862252eea312fbafdd84/papaparse.js#L1484

papaparse definitely has stream parsing modes but iiuc they're not used here.

sorry currently on phone, so cant step through the code :)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug Something isn't working needs triage
Projects
None yet
Development

No branches or pull requests

2 participants