feat(bench): add throughput benchmark harness
This commit is contained in:
1 parent
e649582f81
commit
b32e20d842
1 file changed
+121
+121
@@ -0,0 +1,121 @@
|
||||
const std = @import("std");
|
||||
const cobalt = @import("root.zig");
|
||||
|
||||
fn throughput(ops: usize, elapsedNs: u64) f64 {
|
||||
const seconds = @as(f64, @floatFromInt(elapsedNs)) / std.time.ns_per_s;
|
||||
if (seconds == 0) return 0;
|
||||
return @as(f64, @floatFromInt(ops)) / seconds;
|
||||
}
|
||||
|
||||
fn report(writer: anytype, label: []const u8, ops: usize, elapsedNs: u64) !void {
|
||||
const ms = @as(f64, @floatFromInt(elapsedNs)) / std.time.ns_per_ms;
|
||||
try writer.print("{s:<14} {d:>9} ops in {d:>9.2} ms => {d:>12.0} ops/s\n", .{ label, ops, ms, throughput(ops, elapsedNs) });
|
||||
}
|
||||
|
||||
pub fn main() !void {
|
||||
var gpa = std.heap.GeneralPurposeAllocator(.{}){};
|
||||
defer _ = gpa.deinit();
|
||||
const allocator = gpa.allocator();
|
||||
const stdout = std.io.getStdOut().writer();
|
||||
|
||||
var opCount: usize = 100_000;
|
||||
{
|
||||
var args = try std.process.argsWithAllocator(allocator);
|
||||
defer args.deinit();
|
||||
_ = args.next();
|
||||
if (args.next()) |arg| opCount = std.fmt.parseInt(usize, arg, 10) catch opCount;
|
||||
}
|
||||
|
||||
const dataDir = "cobalt-bench-data";
|
||||
std.fs.cwd().deleteTree(dataDir) catch {};
|
||||
defer std.fs.cwd().deleteTree(dataDir) catch {};
|
||||
|
||||
var db = try cobalt.Db.open(allocator, dataDir, .{ .flushThreshold = 1024 * 1024 });
|
||||
defer db.close();
|
||||
|
||||
try stdout.print("== cobalt benchmark ==\n", .{});
|
||||
try stdout.print("operations: {d}, deterministic keys\n\n", .{opCount});
|
||||
|
||||
var keyBuf: [32]u8 = undefined;
|
||||
var valBuf: [96]u8 = undefined;
|
||||
var timer = try std.time.Timer.start();
|
||||
|
||||
var i: usize = 0;
|
||||
while (i < opCount) : (i += 1) {
|
||||
const key = try std.fmt.bufPrint(&keyBuf, "key:{d:0>12}", .{i});
|
||||
const value = try std.fmt.bufPrint(&valBuf, "value-{d:0>12}-cobalt-lsm-payload", .{i});
|
||||
try db.put(key, value);
|
||||
}
|
||||
try report(stdout, "put", opCount, timer.read());
|
||||
|
||||
const batchSize = 1000;
|
||||
timer.reset();
|
||||
var batched: usize = 0;
|
||||
while (batched < opCount) : (batched += batchSize) {
|
||||
var batch = cobalt.WriteBatch.init(allocator);
|
||||
defer batch.deinit();
|
||||
var j: usize = 0;
|
||||
while (j < batchSize and batched + j < opCount) : (j += 1) {
|
||||
const idx = opCount + batched + j;
|
||||
const key = try std.fmt.bufPrint(&keyBuf, "bat:{d:0>12}", .{idx});
|
||||
const value = try std.fmt.bufPrint(&valBuf, "value-{d:0>12}-cobalt-lsm-payload", .{idx});
|
||||
try batch.put(key, value);
|
||||
}
|
||||
try db.apply(&batch);
|
||||
}
|
||||
try report(stdout, "batch-put", opCount, timer.read());
|
||||
|
||||
timer.reset();
|
||||
try db.maybeCompact();
|
||||
try report(stdout, "compact", db.sstables.items.len, timer.read());
|
||||
|
||||
timer.reset();
|
||||
var found: usize = 0;
|
||||
i = 0;
|
||||
while (i < opCount) : (i += 1) {
|
||||
const key = try std.fmt.bufPrint(&keyBuf, "key:{d:0>12}", .{i});
|
||||
if (try db.get(allocator, key)) |value| {
|
||||
found += 1;
|
||||
allocator.free(value);
|
||||
}
|
||||
}
|
||||
try report(stdout, "get", opCount, timer.read());
|
||||
|
||||
timer.reset();
|
||||
var scanned: usize = 0;
|
||||
{
|
||||
var iter = try db.scan(allocator, null, null);
|
||||
defer iter.deinit();
|
||||
while (try iter.next()) |_| scanned += 1;
|
||||
}
|
||||
try report(stdout, "scan-forward", scanned, timer.read());
|
||||
|
||||
timer.reset();
|
||||
var reverseScanned: usize = 0;
|
||||
{
|
||||
var iter = try db.scanReverse(allocator, .{});
|
||||
defer iter.deinit();
|
||||
while (try iter.next()) |_| reverseScanned += 1;
|
||||
}
|
||||
try report(stdout, "scan-reverse", reverseScanned, timer.read());
|
||||
|
||||
timer.reset();
|
||||
var prefixHits: usize = 0;
|
||||
{
|
||||
var iter = try db.scanPrefix(allocator, "key:00000000000");
|
||||
defer iter.deinit();
|
||||
while (try iter.next()) |_| prefixHits += 1;
|
||||
}
|
||||
try report(stdout, "scan-prefix", prefixHits, timer.read());
|
||||
|
||||
try stdout.print("\nget hits: {d}/{d}, forward/reverse scan entries: {d}/{d}\n", .{ found, opCount, scanned, reverseScanned });
|
||||
|
||||
const stats = db.stats();
|
||||
try stdout.writeAll("\n== database statistics ==\n");
|
||||
try stats.print(stdout);
|
||||
try stdout.print("compression: {d} live entries in {d} physical bytes ({d:.1} bytes/entry)\n", .{
|
||||
stats.liveEntries,
|
||||
stats.physicalBytes,
|
||||
if (stats.liveEntries == 0) 0 else @as(f64, @floatFromInt(stats.physicalBytes)) / @as(f64, @floatFromInt(stats.liveEntries)),
|
||||
});
|
||||
}
|
||||
Reference in new issue
Block a user