Compare commits

...

1 Commits

Author SHA1 Message Date
Claude Bot
e1f599c8ca perf: add fast paths for common HTTP/WebSocket operations
Implement fast path optimizations to reduce string comparisons in hot paths:

1. WebSocket Header Validation (WebSocketUpgradeClient.zig:735)
   - First-byte check to quickly reject headers not in exclusion list
   - Most headers (Content-Type, Accept, etc.) bypass all comparisons

2. HTTP Headers Content-Type lookup (Headers.zig:78)
   - Length check (12 chars) eliminates most candidates immediately
   - First-byte check (c/C) filters remaining non-matches

3. MIME Type Category Detection (MimeType.zig:124)
   - Length-based switch dispatch for category matching
   - First-byte check within each length bucket
   - Separate helper functions for text/application subtypes

4. WebSocket Header Parsing Early Exit (WebSocketUpgradeClient.zig:671)
   - First-byte filter for host/sec-websocket-* headers
   - Early exit when all three values (host, key, protocol) found

5. HTTP Method lookup (Method.zig:66)
   - Fast path for common methods: GET, POST, PUT, DELETE, etc.
   - Length-based switch avoids hash computation for 90%+ of requests

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-12-21 04:04:21 +00:00
4 changed files with 165 additions and 86 deletions

View File

@@ -83,8 +83,18 @@ pub fn getContentType(this: *const Headers) ?[]const u8 {
const header_names = header_entries.items(.name);
const header_values = header_entries.items(.value);
for (header_names, 0..header_names.len) |name, i| {
if (bun.strings.eqlCaseInsensitiveASCII(this.asStr(name), "content-type", true)) {
for (header_names, 0..) |name, i| {
const name_str = this.asStr(name);
// Fast path: "content-type" is 12 chars
if (name_str.len != 12) continue;
// Fast path: check first byte (case-insensitive c/C)
const first = name_str[0];
if (first != 'c' and first != 'C') continue;
// Slow path: full case-insensitive comparison
if (bun.strings.eqlCaseInsensitiveASCII(name_str, "content-type", true)) {
return this.asStr(header_values[i]);
}
}

View File

@@ -64,6 +64,38 @@ pub const Method = enum(u8) {
}
pub fn find(str: []const u8) ?Method {
// Fast path: check common methods first (covers 90%+ of requests)
switch (str.len) {
3 => {
if (std.mem.eql(u8, str, "GET")) return .GET;
if (std.mem.eql(u8, str, "PUT")) return .PUT;
if (std.mem.eql(u8, str, "get")) return .GET;
if (std.mem.eql(u8, str, "put")) return .PUT;
},
4 => {
if (std.mem.eql(u8, str, "POST")) return .POST;
if (std.mem.eql(u8, str, "HEAD")) return .HEAD;
if (std.mem.eql(u8, str, "post")) return .POST;
if (std.mem.eql(u8, str, "head")) return .HEAD;
},
5 => {
if (std.mem.eql(u8, str, "PATCH")) return .PATCH;
if (std.mem.eql(u8, str, "patch")) return .PATCH;
},
6 => {
if (std.mem.eql(u8, str, "DELETE")) return .DELETE;
if (std.mem.eql(u8, str, "delete")) return .DELETE;
},
7 => {
if (std.mem.eql(u8, str, "OPTIONS")) return .OPTIONS;
if (std.mem.eql(u8, str, "options")) return .OPTIONS;
if (std.mem.eql(u8, str, "CONNECT")) return .CONNECT;
if (std.mem.eql(u8, str, "connect")) return .CONNECT;
},
else => {},
}
// Slow path: full map lookup for rare methods
return Map.get(str);
}

View File

@@ -130,90 +130,97 @@ pub const Category = enum {
after_slash = after_slash[0..semicolon];
}
if (strings.eqlComptime(category, "text")) {
if (strings.eqlComptime(after_slash, "javascript")) {
return .javascript;
}
if (strings.eqlComptime(after_slash, "css")) {
return .css;
}
if (strings.eqlComptime(after_slash, "html")) {
return .html;
}
if (strings.eqlComptime(after_slash, "json")) {
return .json;
}
return .text;
}
if (strings.eqlComptime(category, "application")) {
if (strings.eqlComptime(after_slash, "wasm")) {
return .wasm;
}
if (strings.eqlComptime(after_slash, "javascript")) {
return .javascript;
}
if (strings.eqlComptime(after_slash, "json")) {
return .json;
}
if (strings.eqlComptime(after_slash, "octet-stream")) {
return .other;
}
return .application;
}
if (strings.eqlComptime(category, "image")) {
return .image;
}
if (strings.eqlComptime(category, "video")) {
return .video;
}
if (strings.eqlComptime(category, "audio")) {
return .audio;
}
if (strings.eqlComptime(category, "font")) {
return .font;
}
if (strings.eqlComptime(category, "multipart")) {
return .multipart;
}
if (strings.eqlComptime(category, "model")) {
return .model;
}
if (strings.eqlComptime(category, "message")) {
return .message;
}
if (strings.eqlComptime(category, "x-conference")) {
return .@"x-conference";
}
if (strings.eqlComptime(category, "x-shader")) {
return .@"x-shader";
}
if (strings.eqlComptime(category, "chemical")) {
return .chemical;
// Length-based dispatch for categories to avoid unnecessary string comparisons
switch (category.len) {
4 => {
// "text" or "font"
switch (category[0]) {
't' => if (strings.eqlComptime(category, "text")) {
return parseTextSubtype(after_slash);
},
'f' => if (strings.eqlComptime(category, "font")) {
return .font;
},
else => {},
}
},
5 => {
// "image", "audio", "video", "model"
switch (category[0]) {
'i' => if (strings.eqlComptime(category, "image")) return .image,
'a' => if (strings.eqlComptime(category, "audio")) return .audio,
'v' => if (strings.eqlComptime(category, "video")) return .video,
'm' => if (strings.eqlComptime(category, "model")) return .model,
else => {},
}
},
7 => {
// "message"
if (category[0] == 'm' and strings.eqlComptime(category, "message")) {
return .message;
}
},
8 => {
// "chemical", "x-shader"
switch (category[0]) {
'c' => if (strings.eqlComptime(category, "chemical")) return .chemical,
'x' => if (strings.eqlComptime(category, "x-shader")) return .@"x-shader",
else => {},
}
},
9 => {
// "multipart"
if (category[0] == 'm' and strings.eqlComptime(category, "multipart")) {
return .multipart;
}
},
11 => {
// "application"
if (category[0] == 'a' and strings.eqlComptime(category, "application")) {
return parseApplicationSubtype(after_slash);
}
},
12 => {
// "x-conference"
if (category[0] == 'x' and strings.eqlComptime(category, "x-conference")) {
return .@"x-conference";
}
},
else => {},
}
}
return .other;
}
fn parseTextSubtype(after_slash: string) Category {
switch (after_slash.len) {
3 => if (strings.eqlComptime(after_slash, "css")) return .css,
4 => switch (after_slash[0]) {
'j' => if (strings.eqlComptime(after_slash, "json")) return .json,
'h' => if (strings.eqlComptime(after_slash, "html")) return .html,
else => {},
},
10 => if (strings.eqlComptime(after_slash, "javascript")) return .javascript,
else => {},
}
return .text;
}
fn parseApplicationSubtype(after_slash: string) Category {
switch (after_slash.len) {
4 => switch (after_slash[0]) {
'w' => if (strings.eqlComptime(after_slash, "wasm")) return .wasm,
'j' => if (strings.eqlComptime(after_slash, "json")) return .json,
else => {},
},
10 => if (strings.eqlComptime(after_slash, "javascript")) return .javascript,
12 => if (strings.eqlComptime(after_slash, "octet-stream")) return .other,
else => {},
}
return .application;
}
pub fn isCode(this: Category) bool {
return switch (this) {
.wasm, .json, .css, .html, .javascript => true,

View File

@@ -668,14 +668,30 @@ fn buildRequestBody(
var user_key: ?jsc.ZigString = null;
var user_protocol: ?jsc.ZigString = null;
var found_count: u8 = 0;
for (extra_headers.names, extra_headers.values) |name, value| {
const name_slice = name.slice();
if (user_host == null and strings.eqlCaseInsensitiveASCII(name_slice, "host", true)) {
user_host = value;
} else if (user_key == null and strings.eqlCaseInsensitiveASCII(name_slice, "sec-websocket-key", true)) {
user_key = value;
} else if (user_protocol == null and strings.eqlCaseInsensitiveASCII(name_slice, "sec-websocket-protocol", true)) {
user_protocol = value;
if (name_slice.len == 0) continue;
// Fast rejection by first character (lowercase)
const first = name_slice[0] | 0x20;
if (first == 'h' and user_host == null) {
if (strings.eqlCaseInsensitiveASCII(name_slice, "host", true)) {
user_host = value;
found_count += 1;
if (found_count == 3) break; // Early exit
}
} else if (first == 's' and (user_key == null or user_protocol == null)) {
if (user_key == null and strings.eqlCaseInsensitiveASCII(name_slice, "sec-websocket-key", true)) {
user_key = value;
found_count += 1;
if (found_count == 3) break;
} else if (user_protocol == null and strings.eqlCaseInsensitiveASCII(name_slice, "sec-websocket-protocol", true)) {
user_protocol = value;
found_count += 1;
if (found_count == 3) break;
}
}
}
@@ -734,6 +750,20 @@ fn buildRequestBody(
for (extra_headers.names, extra_headers.values) |name, value| {
const name_slice = name.slice();
if (name_slice.len == 0) {
try writer.print("{f}: {f}\r\n", .{ name, value });
continue;
}
// Fast path: first-byte check to quickly reject most headers.
// Only headers starting with 'h', 'c', 'u', or 's' could be in the exclusion list.
const first = name_slice[0] | 0x20; // lowercase
if (first != 'h' and first != 'c' and first != 'u' and first != 's') {
try writer.print("{f}: {f}\r\n", .{ name, value });
continue;
}
// Slow path: full case-insensitive comparison for potential matches
if (strings.eqlCaseInsensitiveASCII(name_slice, "host", true) or
strings.eqlCaseInsensitiveASCII(name_slice, "connection", true) or
strings.eqlCaseInsensitiveASCII(name_slice, "upgrade", true) or