Skip to content

Conversation

giacomocavalieri
Copy link
Member

@giacomocavalieri giacomocavalieri commented Sep 15, 2025

Now that support for bit array pattern matching is the same on both targets we can rewrite the http parsing using case expressions instead of slicing.
I've also been careful in optimising the usage of bit arrays here, to make sure it's always reusing match context as much as possible on the Erlang target: analysing the code with ERL_COMPILER_OPTIONS=bin_opt_info the only calls that cannot be optimised are the ones where we have to stop the parsing and ask for more data, which is impossible to avoid. The happy path however never does any needless materialisation of a matching context!

The performance improvement is quite nice. Here's measurements when using parse_multipart_body to parse a body in its entirety:

Name   ips        average   deviation    median    99th %   Memory usage
new    380.78 K   2.63 μs    ±215.68%   2.54 μs   3.38 μs        0.26 KB
old    135.66 K   7.37 μs     ±38.95%   7.17 μs   9.67 μs       38.72 KB
old is   2.81x  slower +4.75 μs
    uses 150.18x memory +38.46 KB

The code that goes over the entire body is this:

fn parse_all(body: BitArray, boundary: String) -> Nil {
  case http.parse_multipart_body(body, boundary) {
    Ok(http.MultipartBody(chunk: _, done: True, remaining:)) ->
      parse_all_old(remaining, boundary)
    Ok(http.MultipartBody(chunk: _, done: False, remaining: _)) -> Nil
    Ok(http.MoreRequiredForBody(..)) -> panic as "incomplete"
    Error(_) -> panic as "error"
  }
}

Finaly the body I tested it with is built like this:

let middle =
    "--2a8ae6ad-f4ad-4d9a-a92c-6d217011fe0f\r
\r
something something.\r
something something.\r
something something.\r
something something.\r
something something.\r
something something.\r
something something.\r
something something.\r
something something.\r
something something.\r
something something.\r
something something.\r
something something.\r
"
  |> string.repeat(100)

let body = <<
  "This is the preamble.  It is to be ignored, though it\r
is a handy place for mail composers to include an\r
explanatory note to non-MIME compliant readers.\r",
  middle:utf8,
  "--2a8ae6ad-f4ad-4d9a-a92c-6d217011fe0f--\r
This is the epilogue.  It is also to be ignored.",
>>

@giacomocavalieri giacomocavalieri marked this pull request as ready for review September 19, 2025 16:17
Copy link
Member

@lpil lpil left a comment

Choose a reason for hiding this comment

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

Wowwww thanks Jak!! Wanna note in the changelog it is now faster?

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