Skip to content

Commit

Permalink
huffman dirty optimization
Browse files Browse the repository at this point in the history
358.7ms
  • Loading branch information
ianic committed Dec 30, 2023
1 parent 0c9ebd9 commit 075d9a1
Show file tree
Hide file tree
Showing 3 changed files with 45 additions and 19 deletions.
2 changes: 1 addition & 1 deletion readme.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ References:
[Bill Bird Video series](https://www.youtube.com/watch?v=SJPvNi4HrWQ&t)
[RFC](https://datatracker.ietf.org/doc/html/rfc1951)
[zlib algorithm explained](https://github.com/madler/zlib/blob/643e17b7498d12ab8d15565662880579692f769d/doc/algorithm.txt)
[Mark Adler on stackoverflow](https://stackoverflow.com/search?q=user%3A1180620+deflate)
[Mark Adler on stackoverflow](https://stackoverflow.com/search?q=user%3A1180620+deflate)
[Faster zlib/DEFLATE](https://dougallj.wordpress.com/2022/08/20/faster-zlib-deflate-decompression-on-the-apple-m1-and-x86/)
[Reading bits with zero refill latency](https://dougallj.wordpress.com/2022/08/26/reading-bits-with-zero-refill-latency/)
[the canterbury corpus](https://corpus.canterbury.ac.nz/descriptions/)
15 changes: 5 additions & 10 deletions src/benchmark.zig
Original file line number Diff line number Diff line change
Expand Up @@ -2,19 +2,19 @@ const std = @import("std");
const inflate = @import("inflate.zig").inflate;
const assert = std.debug.assert;

// const data = @embedFile("testdata/bb0f7d55e8c50e379fa9bdcb8758d89d08e0cc1f.tar.gz");
// const data_bytes = 177244160;
const data = @embedFile("testdata/bb0f7d55e8c50e379fa9bdcb8758d89d08e0cc1f.tar.gz");
const data_bytes = 177244160;

// const data = @embedFile("testdata/2600.txt.utf-8.gz");
// const data_bytes = 3359630;
//
// const data = @embedFile("testdata/cantrbry.tar.gz");
// const data_bytes = 2821120;

const data = @embedFile("testdata/large.tar.gz");
const data_bytes = 11162624;
// const data = @embedFile("testdata/large.tar.gz");
// const data_bytes = 11162624;

const buffer_len = 1024 * 4;
const buffer_len = 1024 * 64;

fn usage() void {
std.debug.print(
Expand Down Expand Up @@ -72,11 +72,6 @@ fn readerInterface() !void {
var inf = inflate(fbs.reader());
var n: usize = 0;

// while (try inf.nextChunk()) |buf| {
// n += buf.len;
// }
// assert(n == data_bytes);

var buf: [buffer_len]u8 = undefined;
var rdr = inf.reader();
while (true) {
Expand Down
47 changes: 39 additions & 8 deletions src/huffman.zig
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
const std = @import("std");
const testing = std.testing;

const Symbol = struct {
const Symbol = packed struct {
symbol: u16, // symbol from alphabet
code_bits: u4, // code bits count

Expand All @@ -26,7 +26,6 @@ pub fn Huffman(comptime alphabet_size: u16) type {
else => unreachable,
};
const small_lookup_shift = max_code_bits - small_lookup_bits;
const lookup_not_found = 0xffff;

return struct {
// all symbols in alaphabet, sorted by code_len, symbol
Expand All @@ -51,35 +50,49 @@ pub fn Huffman(comptime alphabet_size: u16) type {
// reference: https://youtu.be/9_YEGLe33NA?list=PLU4IQLU9e_OrY8oASHx0u3IXAL9TOdidm&t=2639
var code: u16 = 0;
var code_s: u16 = 0;
for (self.symbols, 0..) |sym, i| {
for (self.symbols) |sym| {
if (sym.code_bits == 0) continue; // skip unused

const next = code + (@as(u16, 1) << (max_code_bits - sym.code_bits));

const u: u16 = (@as(u16, @intCast(sym.code_bits)) << 12) + sym.symbol;

if (sym.code_bits <= small_lookup_bits) {
const next_s = next >> small_lookup_shift;
for (code_s..next_s) |j|
self.lookup_s[j] = @intCast(i);
self.lookup_s[j] = u; //sym;
code_s = next_s;
} else {
// assign symbol index to all codes between current and next code
for (code..next) |j|
self.lookup[j] = @intCast(i);
self.lookup[j] = u;
}
code = next;
}
for (code_s..self.lookup_s.len) |i|
// self.lookup_s[i].code_bits = 0;
self.lookup_s[i] = lookup_not_found;
}

const lookup_not_found = 0xffff;

/// Finds symbol for lookup table code.
pub inline fn find(self: *Self, code: u16) Symbol {
if (small_lookup_bits > 0) {
const code_s = code >> small_lookup_shift;
const idx = self.lookup_s[code_s];
if (idx != lookup_not_found) return self.symbols[idx];
//const sym = self.lookup_s[code_s];
//if (sym.code_bits != 0) return sym;
const u = self.lookup_s[code_s];
if (u != lookup_not_found) return Symbol{
.symbol = u & 0x01_ff,
.code_bits = @intCast(u >> 12),
};
}
return self.symbols[self.lookup[code]];
const u: u16 = self.lookup[code];
return Symbol{
.symbol = u & 0x01_ff,
.code_bits = @intCast(u >> 12),
};
}
};
}
Expand Down Expand Up @@ -158,3 +171,21 @@ test "Huffman init/find" {
for (0b1111_000..0b1_0000_000) |c| // 120...128 (8)
try testing.expectEqual(@as(u16, 16), h.find(@intCast(c)).symbol);
}

test "koliko to ima" {
std.debug.print("\n{d}\n", .{@bitSizeOf(Symbol)});
std.debug.print("{d}\n", .{@sizeOf(Symbol)});
std.debug.print("{d}\n", .{@offsetOf(Symbol, "code_bits")});
std.debug.print("{d}\n", .{@bitOffsetOf(Symbol, "code_bits")});

std.debug.print("\n{d}\n", .{@bitSizeOf(Symbol2)});
std.debug.print("{d}\n", .{@sizeOf(Symbol2)});
std.debug.print("{d}\n", .{@offsetOf(Symbol2, "code_bits")});
std.debug.print("{d}\n", .{@bitOffsetOf(Symbol2, "code_bits")});
}

const Symbol2 = packed struct {
symbol: u8, // symbol from alphabet
code_bits: u4, // code bits count
typ: u1,
};

0 comments on commit 075d9a1

Please sign in to comment.