Skip to content

Commit 61d32f8

Browse files
ericproulxclaude
andcommitted
Avoid per-entry array allocation in Request#build_headers
`build_headers` walked the env with `each_header.with_object(...)`. Because `Enumerator#with_object` hands the block a single value plus the memo, the two values `each_header` yields (`k`, `v`) get boxed into a throwaway `[k, v]` Array on every iteration — which the `|(k, v), headers|` destructure then immediately unpacks. That is one array allocated, packed, destructured, and discarded per request header. Drop `with_object` for a plain `each_header do |k, v|` block writing into a pre-built `Grape::Util::Header`. `k` and `v` arrive as separate block args (normal multi-value yield), so no array is boxed, and the accumulator is just a closed-over local. Output is byte-identical (`each_header` is `Rack::Request::Env#each_header`, i.e. full env iteration; the `HTTP_` filter is unchanged). `build_headers` runs lazily, only when an endpoint reads `headers`. Measured on a request with ~30 headers: ~43% fewer objects (37 -> 21 per call) and ~1.36x faster, with the array packing — not the Enumerator object itself — accounting for the entire gap. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
1 parent 6995daf commit 61d32f8

1 file changed

Lines changed: 3 additions & 1 deletion

File tree

lib/grape/request.rb

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -178,12 +178,14 @@ def make_params
178178
end
179179

180180
def build_headers
181-
each_header.with_object(Grape::Util::Header.new) do |(k, v), headers|
181+
headers = Grape::Util::Header.new
182+
each_header do |k, v|
182183
next unless k.start_with? 'HTTP_'
183184

184185
transformed_header = KNOWN_HEADERS.fetch(k) { -k[5..].tr('_', '-').downcase }
185186
headers[transformed_header] = v
186187
end
188+
headers
187189
end
188190
end
189191
end

0 commit comments

Comments
 (0)