Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/workflows/zig.yml → .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ jobs:
- uses: actions/checkout@v6
- uses: mlugg/setup-zig@v2
with:
version: 0.15.2
version: 0.16.0
- run: zig build test --summary all
lint:
runs-on: ubuntu-latest
Expand Down
71 changes: 22 additions & 49 deletions bench/h264_sps.zig
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
const std = @import("std");
const zbench = @import("zbench");
const h264 = @import("media").h264;

const sps_nal = [_]u8{
Expand All @@ -23,59 +24,31 @@ const sps_with_frame_cropping = [_]u8{
0x20,
};

const iterations = 1_000_000;

pub fn main() !void {
var buffer: [1024]u8 = undefined;
var stdout = std.fs.File.stdout().writer(&buffer);

try stdout.interface.writeAll("\x1b[1;36m┌─────────────────────────┐\x1b[0m\n");
try stdout.interface.writeAll("\x1b[1;36m│ H264 SPS Benchmarks │\x1b[0m\n");
try stdout.interface.writeAll("\x1b[1;36m└─────────────────────────┘\x1b[0m\n\n");

// Warm-up: one pass to bring code/data into cache.
for (0..iterations) |_| {
const sps = try h264.Sps.parse(sps_nal[1..]);
std.mem.doNotOptimizeAway(sps);
}

const fixtures = [_]struct {
name: []const u8,
data: []const u8,
}{
.{ .name = "Basic SPS", .data = sps_nal[1..] },
.{ .name = "SPS with scaling list", .data = sps_with_scaling_list[1..] },
.{ .name = "SPS with frame cropping", .data = sps_with_frame_cropping[1..] },
};

for (fixtures) |fixture| {
try benchMark(fixture.name, fixture.data, &stdout.interface);
}
fn benchBasicSps(allocator: std.mem.Allocator) void {
_ = allocator;
const sps = h264.Sps.parse(sps_nal[1..]) catch unreachable;
std.mem.doNotOptimizeAway(sps);
}

try stdout.interface.flush();
fn benchScalingList(allocator: std.mem.Allocator) void {
_ = allocator;
const sps = h264.Sps.parse(sps_with_scaling_list[1..]) catch unreachable;
std.mem.doNotOptimizeAway(sps);
}

fn benchMark(name: []const u8, data: []const u8, writer: *std.Io.Writer) !void {
var timer = try std.time.Timer.start();
fn benchFrameCropping(allocator: std.mem.Allocator) void {
_ = allocator;
const sps = h264.Sps.parse(sps_with_frame_cropping[1..]) catch unreachable;
std.mem.doNotOptimizeAway(sps);
}

for (0..iterations) |_| {
const sps = try h264.Sps.parse(data);
std.mem.doNotOptimizeAway(sps);
}
pub fn main(init: std.process.Init) !void {
var bench = zbench.Benchmark.init(init.gpa, .{});
defer bench.deinit();

const elapsed_ns = timer.read();
const ns_per_op = elapsed_ns / iterations;
const ops_per_sec = @as(u64, std.time.ns_per_s) / @max(ns_per_op, 1);
try bench.add("H264 Basic SPS", benchBasicSps, .{});
try bench.add("H264 SPS with scaling list", benchScalingList, .{});
try bench.add("H264 SPS with frame cropping", benchFrameCropping, .{});

try writer.print("\x1b[1;33mH264 {s}\x1b[0m\n" ++
" iterations : {d}\n" ++
" total time : {d} ms\n" ++
" ns/op : {d}\n" ++
" ops/sec : {d}\n\n", .{
name,
iterations,
elapsed_ns / std.time.ns_per_ms,
ns_per_op,
ops_per_sec,
});
try bench.run(init.io, std.Io.File.stdout());
}
9 changes: 8 additions & 1 deletion build.zig
Original file line number Diff line number Diff line change
Expand Up @@ -17,10 +17,16 @@ pub fn build(b: *std.Build) void {
test_step.dependOn(&run_media_tests.step);

{
const zbench_dep = b.dependency("zbench", .{
.target = target,
.optimize = .ReleaseFast,
});
const zbench_mod = zbench_dep.module("zbench");

const bench_step = b.step("bench", "Run all benchmarks");

const benches = .{
.{ .name = "h264_sps", .src = "bench/core/h264_sps.zig" },
.{ .name = "h264_sps", .src = "bench/h264_sps.zig" },
};

inline for (benches) |bench| {
Expand All @@ -32,6 +38,7 @@ pub fn build(b: *std.Build) void {
.optimize = .ReleaseFast,
.imports = &.{
.{ .name = "media", .module = mod },
.{ .name = "zbench", .module = zbench_mod },
},
}),
});
Expand Down
9 changes: 7 additions & 2 deletions build.zig.zon
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,13 @@
.name = .media,
.version = "0.1.0",
.fingerprint = 0x6a2ca10c089dcfa9, // Changing this has security and trust implications.
.minimum_zig_version = "0.15.2",
.dependencies = .{},
.minimum_zig_version = "0.16.0",
.dependencies = .{
.zbench = .{
.url = "https://github.com/hendriknielaender/zBench/archive/zig-0.16.0.tar.gz",
.hash = "zbench-0.11.2-YTdc76Q_AQAxonIKZ2-H1PdcESJGwyuYylw6RkPiBqyx",
},
},
.paths = .{
"build.zig",
"build.zig.zon",
Expand Down
34 changes: 15 additions & 19 deletions src/buffer_pool_allocator.zig
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ const Bucket = struct {

const Block = struct { next: ?*Block };

fn init(allocator: std.mem.Allocator, block_size: usize, block_count: usize) !Bucket {
fn init(allocator: std.mem.Allocator, block_size: usize, block_count: usize) std.mem.Allocator.Error!Bucket {
const total_size = block_size * block_count;
const buffer = try allocator.alloc(u8, total_size);

Expand Down Expand Up @@ -78,23 +78,18 @@ pub fn BufferPoolAllocator(comptime config: Config) type {

return struct {
const have_mutex = config.thread_safe;
const mutex_init = if (have_mutex) std.Thread.Mutex{} else DummyMutex{};

const DummyMutex = struct {
inline fn lock(_: DummyMutex) void {}
inline fn unlock(_: DummyMutex) void {}
};
const Mutex = if (have_mutex) std.Io.Mutex else void;

buckets: [config.bucket_sizes.len]Bucket,
backing_allocator: std.mem.Allocator,
buffer_ref_allocator: BufferRefAllocator,
mutex: @TypeOf(mutex_init) = mutex_init,
mutex: Mutex = if (have_mutex) std.Io.Mutex.init else {},

pub fn init(backing_allocator: std.mem.Allocator) !@This() {
var self = @This(){
.backing_allocator = backing_allocator,
.buckets = undefined,
.buffer_ref_allocator = BufferRefAllocator.init(backing_allocator),
.buffer_ref_allocator = BufferRefAllocator.empty,
};
var initialized: usize = 0;
errdefer {
Expand All @@ -115,7 +110,7 @@ pub fn BufferPoolAllocator(comptime config: Config) type {
for (0..config.bucket_sizes.len) |idx| {
self.buckets[idx].deinit(self.backing_allocator);
}
self.buffer_ref_allocator.deinit();
self.buffer_ref_allocator.deinit(self.backing_allocator);
}

pub fn allocator(self: *@This()) std.mem.Allocator {
Expand All @@ -133,18 +128,18 @@ pub fn BufferPoolAllocator(comptime config: Config) type {
fn alloc(context: *anyopaque, len: usize, _: std.mem.Alignment, _: usize) ?[*]u8 {
const self: *@This() = @ptrCast(@alignCast(context));
if (len == buffer_ref_size) {
if (have_mutex) std.Thread.Mutex.lock(&self.mutex);
defer if (have_mutex) std.Thread.Mutex.unlock(&self.mutex);
const buf_ref = self.buffer_ref_allocator.create() catch {
if (have_mutex) std.Io.Threaded.mutexLock(&self.mutex);
defer if (have_mutex) std.Io.Threaded.mutexUnlock(&self.mutex);
const buf_ref = self.buffer_ref_allocator.create(self.backing_allocator) catch {
return null;
};
return @ptrCast(@alignCast(buf_ref));
}

for (&self.buckets) |*bucket| {
if (len <= bucket.block_size) {
if (have_mutex) std.Thread.Mutex.lock(&self.mutex);
defer if (have_mutex) std.Thread.Mutex.unlock(&self.mutex);
if (have_mutex) std.Io.Threaded.mutexLock(&self.mutex);
defer if (have_mutex) std.Io.Threaded.mutexUnlock(&self.mutex);

if (bucket.acquire()) |b| {
return b.ptr;
Expand All @@ -157,17 +152,18 @@ pub fn BufferPoolAllocator(comptime config: Config) type {
fn free(context: *anyopaque, memory: []u8, _: std.mem.Alignment, _: usize) void {
const self: *@This() = @ptrCast(@alignCast(context));
if (memory.len == buffer_ref_size) {
if (have_mutex) std.Thread.Mutex.lock(&self.mutex);
defer if (have_mutex) std.Thread.Mutex.unlock(&self.mutex);
if (have_mutex) std.Io.Threaded.mutexLock(&self.mutex);
defer if (have_mutex) std.Io.Threaded.mutexUnlock(&self.mutex);
self.buffer_ref_allocator.destroy(@ptrCast(@alignCast(memory.ptr)));
return;
}
const ptr = @intFromPtr(memory.ptr);
for (&self.buckets) |*bucket| {
const start = @intFromPtr(bucket.buffer.ptr);
if (ptr >= start and ptr < start + bucket.buffer.len) {
if (have_mutex) std.Thread.Mutex.lock(&self.mutex);
defer if (have_mutex) std.Thread.Mutex.unlock(&self.mutex);
if (have_mutex) std.Io.Threaded.mutexLock(&self.mutex);
defer if (have_mutex) std.Io.Threaded.mutexUnlock(&self.mutex);

bucket.release(memory);
return;
}
Expand Down
6 changes: 1 addition & 5 deletions src/h264.zig
Original file line number Diff line number Diff line change
Expand Up @@ -163,11 +163,7 @@ pub const Sps = struct {
const entries: usize = if (sps.chroma_format_idc != 3) 8 else 12;
for (0..entries) |i| {
if (try bit_reader.takeBit() == 0) continue;
if (i < 6) {
try parseScalingList(&bit_reader, 16);
} else {
try parseScalingList(&bit_reader, 64);
}
try parseScalingList(&bit_reader, if (i < 6) 16 else 64);
}
}
},
Expand Down
2 changes: 1 addition & 1 deletion src/io.zig
Original file line number Diff line number Diff line change
Expand Up @@ -207,5 +207,5 @@ pub const BitReader = struct {
};

test {
std.testing.refAllDeclsRecursive(@This());
std.testing.refAllDecls(@This());
}
11 changes: 4 additions & 7 deletions src/root.zig
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ const BufferRef = struct {
.ref_count = .init(1),
};

fn init(buffer_ref: *BufferRef, allocator: Allocator, size: usize) !void {
fn init(buffer_ref: *BufferRef, allocator: Allocator, size: usize) Allocator.Error!void {
buffer_ref.data = try allocator.alloc(u8, size);
}

Expand Down Expand Up @@ -78,7 +78,7 @@ pub const Packet = struct {

/// Allocates an uninitialised owned buffer of `size` bytes.
/// Use `mutableData()` to fill the buffer before sharing the packet.
pub fn alloc(allocator: Allocator, size: usize) !Packet {
pub fn alloc(allocator: Allocator, size: usize) Allocator.Error!Packet {
const buffer_ref = try allocator.create(BufferRef);

buffer_ref.* = .{
Expand All @@ -93,7 +93,7 @@ pub const Packet = struct {
}

/// Allocates an owned buffer and copies `src` into it (analogous to `std.mem.Allocator.dupe`).
pub fn dupe(allocator: Allocator, src: []const u8) !Packet {
pub fn dupe(allocator: Allocator, src: []const u8) Allocator.Error!Packet {
var packet = try alloc(allocator, src.len);
@memcpy(packet.mutableData().?, src);
return packet;
Expand Down Expand Up @@ -278,8 +278,5 @@ test "Packet.mutableData: writes are visible through data slice" {
}

test {
std.testing.refAllDeclsRecursive(@This());
_ = @import("h264.zig");
_ = @import("io.zig");
_ = @import("buffer_pool_allocator.zig");
std.testing.refAllDecls(@This());
}
Loading