Skip to content

Decoding subnormal numbers is really slow #1232

Closed

Description

@nineteendo

Description

I was running benchmarks for different json libaries and noticed that decoding subnormal numbers is 150x slower than orjson (it's even slower than the python implementation of jsonyx which is not shown here):

decode json jsonyx msgspec orjson yyjson fastest time
65,536 subnormal floats 11.27x 11.63x 150.63x 1.00x 1.12x 2508.46 μs

Input data: json.dumps([5e-324] * 65_536).encode().
Full benchmark code: https://github.com/nineteendo/jsonyx/blob/main/bench/__main__.py

Activity

  1. provinzkraut commented on Oct 6, 2026

    @provinzkraut
    Member

    Very interesting. Thanks for reporting this!

  2. provinzkraut commented on Oct 6, 2026

    @provinzkraut
    Member

    Right, so, we generally fall back onto our slow parse_number_fallback parser instead of the fast-path one for everything that's out of range of the ms_atof_powers_of_10 table (exponent range there is range(-307, 289)). We could fix this, but then the fast-path parser would also need to handle subnormals and overflows, which it currently does not.

    It's not too much of an effort (probably) to do this, but I'm unsure whether it's worth it that way. Even with that fix in, there's still other pathological cases (e.g. floats with more than 19 significant digits right on a rounding boundary are an order of magnitude slower than orjson). Maybe we should investigate how yyjson (which is what orson uses under the hood) does here, and take some inspiration from it.

    @sobolevn any opinions?

  3. provinzkraut commented on Oct 6, 2026

    @provinzkraut
    Member
  4. sobolevn commented on Oct 6, 2026

    @sobolevn
    Member

    I would say that copying orjson way of doing this would probably be the best bet :)
    Unless we know how to make this even faster.

  5. provinzkraut commented on Oct 6, 2026

    @provinzkraut
    Member

    I would say that copying orjson way of doing this would probably be the best bet :) Unless we know how to make this even faster.

    Well, yyjson, as that's where it's implemented.

    unless we know how to make this even faster.

    I think we might. If we add the additional 2 fast-paths on top of what we have right now, and speed up our fallback path (something yyjson does, where they don't go straight into the exact decode routine), we could end up faster than orjson in the standard and pathological cases. In theory at least.

  6. nineteendo commented on Nov 1, 2026

    @nineteendo
    Author

    Note that the same slowdown exists for very large floats:

    case json jsonyx fastsimdjson msgspec orjson fastest time
    5e-324 9.29x 8.82x 1.00x 124.77x 1.08x 45.95 ns
    2e-324 13.54x 13.03x 1.00x 151.55x 1.08x 45.02 ns
    1.7976931348623157e+308 8.32x 8.14x 1.00x 183.23x 1.32x 54.56 ns

    Should this get its own issue? Right now I just linked to this one.

  7. sobolevn commented on Nov 1, 2026

    @sobolevn
    Member

    I think that we should consider using https://github.com/kolemannix/ffc.h instead of our own implementation :)

  8. added a commit that references this issue on Nov 13, 2026
    a40ee1a
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions