Skip to content

Add allocator to all public functions to improve performance #4

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
May 7, 2025

Conversation

marselester
Copy link
Owner

@marselester marselester commented May 7, 2025

This allowed to speed up lookups from ~85K/s to ~656K/s by reusing the arena allocator in the benchmark.

examples/benchmark.zig
// The benchmark is contributed by @oschwald.
const std = @import("std");
const maxminddb = @import("maxminddb");

const default_db_path: []const u8 = "GeoLite2-City.mmdb";
const default_num_lookups: u64 = 1_000_000;

pub fn main() !void {
    var gpa = std.heap.GeneralPurposeAllocator(.{}){};
    const allocator = gpa.allocator();
    defer _ = gpa.detectLeaks();

    const args = try std.process.argsAlloc(allocator);
    defer std.process.argsFree(allocator, args);

    var db_path = default_db_path;
    var num_lookups = default_num_lookups;

    if (args.len > 1) {
        db_path = args[1];
    }
    if (args.len > 2) {
        num_lookups = try std.fmt.parseUnsigned(u64, args[2], 10);
    }

    std.debug.print("Benchmarking with:\n", .{});
    std.debug.print("  Database: {s}\n", .{db_path});
    std.debug.print("  Lookups:  {d}\n", .{num_lookups});
    std.debug.print("Opening database...\n", .{});

    var open_timer = try std.time.Timer.start();
    var db = try maxminddb.Reader.mmap(allocator, db_path);
    defer db.unmap();
    const open_time_ms = @as(f64, @floatFromInt(open_timer.read())) /
        @as(f64, @floatFromInt(std.time.ns_per_ms));
    std.debug.print("Database opened successfully in {d} ms. Type: {s}\n", .{
        open_time_ms,
        db.metadata.database_type,
    });

    var arena = std.heap.ArenaAllocator.init(allocator);
    defer arena.deinit();
    const arena_allocator = arena.allocator();

    std.debug.print("Starting benchmark...\n", .{});
    var timer = try std.time.Timer.start();
    var not_found_count: u64 = 0;
    var lookup_errors: u64 = 0;
    var ip_bytes: [4]u8 = undefined;

    for (0..num_lookups) |_| {
        std.crypto.random.bytes(&ip_bytes);
        const ip = std.net.Address.initIp4(ip_bytes, 0);

        _ = db.lookup(arena_allocator, maxminddb.geolite2.City, &ip) catch |err| {
            switch (err) {
                maxminddb.Error.AddressNotFound => {
                    not_found_count += 1;
                    continue;
                },
                else => {
                    std.debug.print("! Lookup error for IP {any}: {any}\n", .{ ip, err });
                    lookup_errors += 1;
                    continue;
                },
            }
        };
        _ = arena.reset(std.heap.ArenaAllocator.ResetMode.retain_capacity);
    }

    const elapsed_ns = timer.read();
    const elapsed_s = @as(f64, @floatFromInt(elapsed_ns)) /
        @as(f64, @floatFromInt(std.time.ns_per_s));
    const lookups_per_second = if (elapsed_s > 0)
        @as(f64, @floatFromInt(num_lookups)) / elapsed_s
    else
        0.0;
    const successful_lookups = num_lookups - not_found_count - lookup_errors;

    std.debug.print("\n--- Benchmark Finished ---\n", .{});
    std.debug.print("Total Lookups Attempted: {d}\n", .{num_lookups});
    std.debug.print("Successful Lookups:      {d}\n", .{successful_lookups});
    std.debug.print("IPs Not Found:           {d}\n", .{not_found_count});
    std.debug.print("Lookup Errors:           {d}\n", .{lookup_errors});
    std.debug.print("Elapsed Time:            {d} s\n", .{elapsed_s});
    std.debug.print("Lookups Per Second (avg):{d}\n", .{lookups_per_second});
}

Download GeoLite2-City.mmdb, add the benchmark to build.zig, and run zig build example_benchmark -Doptimize=ReleaseFast.

new
$ zig build example_benchmark -Doptimize=ReleaseFast
Benchmarking with:
  Database: GeoLite2-City.mmdb
  Lookups:  1000000
Opening database...
Database opened successfully in 0.04715 ms. Type: GeoLite2-City
Starting benchmark...

--- Benchmark Finished ---
Total Lookups Attempted: 1000000
Successful Lookups:      859161
IPs Not Found:           140839
Lookup Errors:           0
Elapsed Time:            1.523236271 s
Lookups Per Second (avg):656496.9722940638
old
zig build example_benchmark -Doptimize=ReleaseFast
Benchmarking with:
  Database: GeoLite2-City.mmdb
  Lookups:  1000000
Opening database...
Database opened successfully in 0.0484 ms. Type: GeoLite2-City
Starting benchmark...

--- Benchmark Finished ---
Total Lookups Attempted: 1000000
Successful Lookups:      859163
IPs Not Found:           140837
Lookup Errors:           0
Elapsed Time:            11.745747762 s
Lookups Per Second (avg):85137.19349867305

This allowed to speed up lookups from ~85K/s to ~650K/s
by reusing the arena allocator in the benchmark.
@marselester marselester changed the title Add allocator param to all public functions to improve performance Add allocator to all public functions to improve performance May 7, 2025
@marselester marselester merged commit 3b23fc9 into master May 7, 2025
1 check passed
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.

1 participant