Skip to content

Commit

Permalink
KeyValue.get performance tweaks
Browse files Browse the repository at this point in the history
Try to improve KeyValue.get performance by adding a u8 hashcode that can be
used as a quick check for possible match. The goal is to get better cache
locality.
  • Loading branch information
karlseguin committed Sep 16, 2024
1 parent c672a63 commit 844300c
Show file tree
Hide file tree
Showing 2 changed files with 23 additions and 12 deletions.
25 changes: 18 additions & 7 deletions src/key_value.zig
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,12 @@ const mem = std.mem;
const ascii = std.ascii;
const Allocator = std.mem.Allocator;

fn KeyValue(K: type, V: type, equalFn: fn (lhs: K, rhs: K) callconv(.Inline) bool) type {
fn KeyValue(K: type, V: type, equalFn: fn (lhs: K, rhs: K) callconv(.Inline) bool, hashFn: fn (key: K) callconv(.Inline) u8) type {
return struct {
len: usize,
keys: []K,
values: []V,
hashes: []u8,

pub const Value = V;

Expand All @@ -19,12 +20,14 @@ fn KeyValue(K: type, V: type, equalFn: fn (lhs: K, rhs: K) callconv(.Inline) boo
.len = 0,
.keys = try allocator.alloc(K, max),
.values = try allocator.alloc(V, max),
.hashes = try allocator.alloc(u8, max),
};
}

pub fn deinit(self: *Self, allocator: Allocator) void {
allocator.free(self.keys);
allocator.free(self.values);
allocator.free(self.hashes);
}

pub fn add(self: *Self, key: K, value: V) void {
Expand All @@ -36,14 +39,15 @@ fn KeyValue(K: type, V: type, equalFn: fn (lhs: K, rhs: K) callconv(.Inline) boo

keys[len] = key;
self.values[len] = value;
self.hashes[len] = hashFn(key);
self.len = len + 1;
}

pub fn get(self: *const Self, needle: K) ?V {
pub fn get(self: *const Self, key: K) ?V {
const hash = hashFn(key);
const keys = self.keys[0..self.len];
for (keys, 0..) |key, i| {
if (equalFn(key, needle)) {
// return key;
for (self.hashes[0..self.len], 0..) |h, i| {
if (h == hash and equalFn(keys[i], key)) {
return self.values[i];
}
}
Expand Down Expand Up @@ -89,17 +93,24 @@ fn KeyValue(K: type, V: type, equalFn: fn (lhs: K, rhs: K) callconv(.Inline) boo
};
}

inline fn strHash(key: []const u8) u8 {
if (key.len == 0) {
return 0;
}
return @as(u8, @truncate(key.len)) | (key[0]) ^ (key[key.len - 1]);
}

inline fn strEql(lhs: []const u8, rhs: []const u8) bool {
return std.mem.eql(u8, lhs, rhs);
}

pub const StringKeyValue = KeyValue([]const u8, []const u8, strEql);
pub const StringKeyValue = KeyValue([]const u8, []const u8, strEql, strHash);

const MultiForm = struct {
value: []const u8,
filename: ?[]const u8 = null,
};
pub const MultiFormKeyValue = KeyValue([]const u8, MultiForm, strEql);
pub const MultiFormKeyValue = KeyValue([]const u8, MultiForm, strEql, strHash);

const t = @import("t.zig");
test "KeyValue: get" {
Expand Down
10 changes: 5 additions & 5 deletions src/url.zig
Original file line number Diff line number Diff line change
Expand Up @@ -124,18 +124,18 @@ pub const Url = struct {
}

pub fn isValid(url: []const u8) bool {
var i: usize = 0;
var rest = url;
if (comptime std.simd.suggestVectorLength(u8)) |vector_len| {
while (i > vector_len) {
const block: @Vector(vector_len, u8) = url[i..][0..vector_len].*;
while (rest.len >= vector_len) {
const block: @Vector(vector_len, u8) = rest[0..vector_len].*;
if (@reduce(.Min, block) < 32 or @reduce(.Max, block) > 126) {
return false;
}
i += vector_len;
rest = rest[vector_len..];
}
}

for (url[i..]) |c| {
for (rest) |c| {
if (c < 32 or c > 126) {
return false;
}
Expand Down

0 comments on commit 844300c

Please sign in to comment.