Skip to content

Better perfomance with different lexer parameters - #208

Merged
Leonidas-from-XIV merged 2 commits into
ocaml-community:masterfrom
ferminr:lexer-perf
Oct 7, 2026
Merged

Leonidas-from-XIV merged 2 commits into
ocaml-community:masterfrom
ferminr:lexer-perf

Conversation

@ferminr

@ferminr ferminr commented Oct 2, 2026 •

Copy link
Copy Markdown

Use -ml in ocamllex and ~with_positions:false for faster lexing.

The tests all pass.

I've benchmarked Yojson.Safe.from_string, for file sizes of 1 kB, 10 kB, .... Using OCaml 5.5, measured in an AMD EPYC 9755, I see the following improvemets:

  ┌────────┬─────────────┬───────────┬────────────┬────────┐
  │ Input  │   master    │ -ml+nopos │ Δ absolute │  Δ %   │
  ├────────┼─────────────┼───────────┼────────────┼────────┤
  │ 1 KB   │ 7.28 µs     │ 4.05 µs   │ −3.23 µs   │ −44.4% │
  ├────────┼─────────────┼───────────┼────────────┼────────┤
  │ 10 KB  │ 98.62 µs    │ 55.82 µs  │ −42.80 µs  │ −43.4% │
  ├────────┼─────────────┼───────────┼────────────┼────────┤
  │ 25 KB  │ 259.67 µs   │ 150.76 µs │ −108.91 µs │ −41.9% │
  ├────────┼─────────────┼───────────┼────────────┼────────┤
  │ 50 KB  │ 553.52 µs   │ 329.26 µs │ −224.26 µs │ −40.5% │
  ├────────┼─────────────┼───────────┼────────────┼────────┤
  │ 100 KB │ 1,171.72 µs │ 737.06 µs │ −434.66 µs │ −37.1% │
  └────────┴─────────────┴───────────┴────────────┴────────┘

In an older Intel i5-8600, improvements are more modest, between 15% and 25% (smaller files always better).

See this gist for how to run the same benchmarks: https://gist.github.com/ferminr/54fb56b4d99be3cd21c5c0fc45c4cef4

Binary size of library yojson.a increases from 1.17 MB to 1.47 MB, due to the lexer generated with -ml.

@Leonidas-from-XIV

Copy link
Copy Markdown
Member

Thanks for the contribution, it's quite an interesting idea!

Looking at the man page of ocamllex it notes the following:

Output code that does not use OCaml's built-in automata interpreter. Instead, the automaton is encoded by OCaml functions. This option is mainly useful for debugging ocamllex(1), using it for production lexers is not recommended.

Could you benchmark with only the ~with_positions:false change?

@ferminr

ferminr commented Oct 6, 2026 •

Copy link
Copy Markdown
Author

Based on [1] and [2], I'd say the man page of ocamllex is outdated.

[1] https://ocaml.org/manual/5.5/lexyacc.html
[2] https://discuss.ocaml.org/t/high-performance-lexing-in-ocaml/7552/5

Benchmarking only the ~with_positions:false change produces no noticeable difference in performance. I think it's because of the "yojson hack" mentioned in [2]. That is, the code to update positions is commented out at https://github.com/ocaml-community/yojson/blob/master/lib/read.mll#L15-L21.

To be more precise. In table mode, we can disable positions updating by providing our own implementation of Lexing.engine, which yojson already does. In -ml mode, positions are updated by code in the generated lexer, and the only way to disable the updating is via with_positions:false.

@Leonidas-from-XIV

Copy link
Copy Markdown
Member

That's a good point, I've sent a PR to the compiler to update the man page. So if I read Gabriel's message correctly with the improvements in 4.08+ there is no need for the lexing engine hack. Could you remove it and see if it changes the performance characteristics?

I would say that anything older than 4.08 is probably not sufficiently relevant these days to keep the hack in.

@ferminr

ferminr commented Oct 6, 2026

Copy link
Copy Markdown
Author

Could you remove it and see if it changes the performance characteristics?

Removed the overrride for Lexing.engine. No changes in performance. Cannot be any other way: if we run ocamllex lib/read.mll, we see calls to Lexing.engine in the generated read.ml. But if we run ocamllex -ml lib/read.mll, we see none.

@Leonidas-from-XIV

Copy link
Copy Markdown
Member

Very good and yes, that makes sense. Can you add a changelog entry? Then it looks good to go from my side.

@Leonidas-from-XIV
Leonidas-from-XIV merged commit df9e87c into ocaml-community:master Oct 7, 2026
2 of 3 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants