diff --git a/benchmark.sh b/benchmark.sh new file mode 100755 index 0000000..4f2b971 --- /dev/null +++ b/benchmark.sh @@ -0,0 +1,17 @@ +#! /usr/bin/env bash + +ARCHIVE="testing/LinuxPATest.sfs" + +REF_EXT_LOC="testing/LinuxPAReference" +PROG_EXT_LOC="testing/LinuxPABinTest" + +echo "Testing Multi-threaded Performance" +echo "" + +hyperfine --warmup 5 --prepare "rm -rf $REF_EXT_LOC && rm -rf $PROG_EXT_LOC" "unsquashfs -d $REF_EXT_LOC $ARCHIVE" "zig-out/bin/unsquashfs -d $PROG_EXT_LOC $ARCHIVE" + +echo "" +echo "Testing Single-threaded Performance" +echo "" + +hyperfine --warmup 5 --prepare "rm -rf $REF_EXT_LOC && rm -rf $PROG_EXT_LOC" "unsquashfs -p 1 -d $REF_EXT_LOC $ARCHIVE" "zig-out/bin/unsquashfs -p 1 -d $PROG_EXT_LOC $ARCHIVE" diff --git a/run_tests.sh b/run_tests.sh deleted file mode 100755 index b3cbdf4..0000000 --- a/run_tests.sh +++ /dev/null @@ -1,10 +0,0 @@ -#!/bin/sh - -zig test \ - -lc \ - -lz \ - -llzma \ - -lminilzo \ - -llz4 \ - -lzstd \ - src/test.zig diff --git a/src/archive.zig b/src/archive.zig index 0b3c270..58b2618 100644 --- a/src/archive.zig +++ b/src/archive.zig @@ -83,7 +83,8 @@ pub fn extract(self: *Archive, alloc: std.mem.Allocator, io: Io, ext_loc: []cons self.super.block_size, self.super.root_ref, ); - return Extract.extract(alloc, io, root_inode, &self.cache, self.super, ext_loc, options); + + return Extract.extractSingleThreaded(alloc, io, root_inode, &self.cache, self.super, ext_loc, options); } // Superblock @@ -126,12 +127,12 @@ pub const Superblock = extern struct { pub fn validate(self: Superblock) !void { if (self.magic != std.mem.readInt(u32, "hsqs", .little)) return error.BadMagic; + if (self.flags.check) + return error.BadCheckFlag; if (self.ver_maj != 4 or self.ver_min != 0) return error.InvalidVersion; if (self.block_log != std.math.log2(self.block_size)) return error.BadBlockLog; - if (self.flags.check) - return error.BadCheckFlag; } }; diff --git a/src/data/reader.zig b/src/data/reader.zig index 78d064a..a7db840 100644 --- a/src/data/reader.zig +++ b/src/data/reader.zig @@ -48,7 +48,7 @@ pub fn init(io: Io, cache: *DecompCache, block_size: u32, size: u64, start: u64, }; } pub fn deinit(self: Reader) void { - self.cache.finished(self.io); + self.cache.finished(self.io, self.cur_offset); } pub fn addFragment(self: *Reader, data: []u8, offset: u32) void { @@ -98,7 +98,7 @@ fn advance(self: *Reader) Io.Reader.Error!void { self.interface.end = size; return; } - const data = self.cache.get(self.io, self.cur_offset, block.size, size); + const data = self.cache.get(self.io, self.cur_offset, block.size, @truncate(size)) catch return error.ReadFailed; if (data.len != size) { std.debug.print("Size of decompression at {} is {} and should be {}\n", .{ self.cur_offset, data.len, size }); return Io.Reader.Error.ReadFailed; diff --git a/src/decomp_cache.zig b/src/decomp_cache.zig index 5d94b2e..6626c21 100644 --- a/src/decomp_cache.zig +++ b/src/decomp_cache.zig @@ -87,7 +87,7 @@ pub fn get(self: *DecompCache, io: Io, offset: u64, compressed_size: u32, max_si pub fn finished(self: *DecompCache, io: Io, offset: u64) void { const cache = self.cache.getPtr(offset); if (cache == null) { - std.debug.print("Finished using cache, but cache does not exist: {}\n", .{offset}); + // std.debug.print("Finished using cache, but cache does not exist: {}\n", .{offset}); return; } diff --git a/src/extract.zig b/src/extract.zig index 6889bd4..1d32d24 100644 --- a/src/extract.zig +++ b/src/extract.zig @@ -2,7 +2,6 @@ const std = @import("std"); const Io = std.Io; const Atomic = std.atomic.Value; -const DataExtractor = @import("data/extractor.zig"); const DataReader = @import("data/reader.zig"); const DecompCache = @import("decomp_cache.zig"); const Directory = @import("directory.zig"); @@ -12,7 +11,7 @@ const Lookup = @import("lookup.zig"); const Superblock = @import("archive.zig").Superblock; const XattrTable = @import("xattr.zig"); -pub fn extract(alloc: std.mem.Allocator, io: Io, inode: Inode, cache: *DecompCache, super: Superblock, ext_loc: []const u8, options: ExtractionOptions) !void { +pub fn extractSingleThreaded(alloc: std.mem.Allocator, io: Io, inode: Inode, cache: *DecompCache, super: Superblock, ext_loc: []const u8, options: ExtractionOptions) !void { const path = std.mem.trim(u8, ext_loc, "/"); var id_table: Lookup.Table(u16) = .init(alloc, cache, super.id_start, super.id_count); @@ -72,7 +71,7 @@ fn extractDir( options: ExtractionOptions, path: []const u8, inode: Inode, -) Error!DirReturn(false) { +) Error!DirReturn { try Io.Dir.cwd().createDirPath(io, path); const dir = inode.directory(alloc, io, cache, super.dir_start) catch |err| switch (err) { @@ -81,7 +80,7 @@ fn extractDir( }; defer dir.deinit(alloc); - const ret: DirReturn(false) = .{ + const ret: DirReturn = .{ .hdr = inode.hdr, .path = path, @@ -122,18 +121,18 @@ fn extractFile( frag_table: *Lookup.Table(Lookup.FragmentEntry), path: []const u8, inode: Inode, -) Error!FileReturn(false) { +) Error!FileReturn { var atomic = try Io.Dir.cwd().createFileAtomic(io, path, .{}); defer atomic.deinit(io); - var ret: FileReturn(false) = .{ + var ret: FileReturn = .{ .hdr = inode.hdr, .path = path, }; - const data: DataExtractor = switch (inode.data) { + var data: DataReader = switch (inode.data) { .file => |f| blk: { - var data: DataExtractor = .init(cache, block_size, f.size, f.data_start, f.blocks); + var data: DataReader = .init(io, cache, block_size, f.size, f.data_start, f.blocks); if (f.frag_idx != 0xFFFFFFFF) { const entry: Lookup.FragmentEntry = try frag_table.get(io, f.frag_idx); if (entry.size.uncompressed) { @@ -146,7 +145,7 @@ fn extractFile( break :blk data; }, .ext_file => |f| blk: { - var data: DataExtractor = .init(cache, block_size, f.size, f.data_start, f.blocks); + var data: DataReader = .init(io, cache, block_size, f.size, f.data_start, f.blocks); if (f.frag_idx != 0xFFFFFFFF) { const entry: Lookup.FragmentEntry = try frag_table.get(io, f.frag_idx); if (entry.size.uncompressed) { @@ -162,7 +161,12 @@ fn extractFile( }, else => unreachable, }; - try data.asyncExtract(io, atomic.file); + defer data.deinit(); + + var wrt_buf: [1024]u8 = undefined; + var wrt = atomic.file.writer(io, &wrt_buf); + _ = try data.interface.streamRemaining(&wrt.interface); + try wrt.flush(); try atomic.link(io); @@ -177,8 +181,8 @@ fn extractSymlink(io: Io, path: []const u8, inode: Inode) Error!void { try Io.Dir.cwd().symLink(io, target, path, .{}); } -fn extractNod(alloc: std.mem.Allocator, path: []const u8, inode: Inode) Error!FileReturn(false) { - var ret: FileReturn(false) = .{ +fn extractNod(alloc: std.mem.Allocator, path: []const u8, inode: Inode) Error!FileReturn { + var ret: FileReturn = .{ .hdr = inode.hdr, .path = path, }; @@ -259,67 +263,25 @@ fn setMetadata(alloc: std.mem.Allocator, io: Io, id_table: *Lookup.Table(u16), x } const Error = error{ Canceled, MknodError, SetXattrError } || Directory.Error || Io.Dir.CreateFileAtomicError || Io.File.Atomic.LinkError || - DataExtractor.Error || Io.Dir.SymLinkError || Io.Dir.CreateDirPathError; + Io.Dir.SymLinkError || Io.Dir.CreateDirPathError || Io.Reader.StreamError || Io.File.SetPermissionsError; -const ReturnUnion = union(enum) { - file: Error!FileReturn, - dir: Error!DirReturn(true), - void: Error!void, +const FileReturn = struct { + hdr: Inode.Header, + path: []const u8, + + xattr_idx: ?u32 = null, + + fn finish(self: FileReturn, alloc: std.mem.Allocator, io: Io, id_table: *Lookup.Table(u16), xattr_table: *XattrTable, options: ExtractionOptions) Error!void { + return setMetadata(alloc, io, id_table, xattr_table, options, self.hdr, self.xattr_idx, self.path); + } }; +const DirReturn = struct { + hdr: Inode.Header, + path: []const u8, -fn FileReturn(comptime async: bool) type { - return if (async) - struct { - hdr: Inode.Header, - path: []const u8, - parent: ?*Atomic(u32), + xattr_idx: ?u32 = null, - xattr_idx: ?u32 = null, - - fn finish(self: FileReturn(async), alloc: std.mem.Allocator, io: Io, id_table: *Lookup.Table(u16), xattr_table: *XattrTable, options: ExtractionOptions) Error!void { - defer if (self.parent != null) { - _ = self.parent.?.fetchSub(1, .acquire); - }; - return setMetadata(alloc, io, id_table, xattr_table, options, self.hdr, self.xattr_idx, self.path); - } - } - else - struct { - hdr: Inode.Header, - path: []const u8, - - xattr_idx: ?u32 = null, - - fn finish(self: FileReturn(async), alloc: std.mem.Allocator, io: Io, id_table: *Lookup.Table(u16), xattr_table: *XattrTable, options: ExtractionOptions) Error!void { - return setMetadata(alloc, io, id_table, xattr_table, options, self.hdr, self.xattr_idx, self.path); - } - }; -} -fn DirReturn(comptime async: bool) type { - return if (async) - struct { - hdr: Inode.Header, - path: []const u8, - parent: ?*Atomic(u32), - - xattr_idx: ?u32 = null, - - fn finish(self: DirReturn(async), alloc: std.mem.Allocator, io: Io, id_table: *Lookup.Table(u16), xattr_table: *XattrTable, options: ExtractionOptions) Error!void { - defer if (self.parent != null) { - _ = self.parent.?.fetchSub(1, .acquire); - }; - return setMetadata(alloc, io, id_table, xattr_table, options, self.hdr, self.xattr_idx, self.path); - } - } - else - struct { - hdr: Inode.Header, - path: []const u8, - - xattr_idx: ?u32 = null, - - fn finish(self: DirReturn(async), alloc: std.mem.Allocator, io: Io, id_table: *Lookup.Table(u16), xattr_table: *XattrTable, options: ExtractionOptions) Error!void { - return setMetadata(alloc, io, id_table, xattr_table, options, self.hdr, self.xattr_idx, self.path); - } - }; -} + fn finish(self: DirReturn, alloc: std.mem.Allocator, io: Io, id_table: *Lookup.Table(u16), xattr_table: *XattrTable, options: ExtractionOptions) Error!void { + return setMetadata(alloc, io, id_table, xattr_table, options, self.hdr, self.xattr_idx, self.path); + } +}; diff --git a/src/file.zig b/src/file.zig index f3e7e7a..21009b7 100644 --- a/src/file.zig +++ b/src/file.zig @@ -103,5 +103,5 @@ pub fn open(self: *SfsFile, alloc: std.mem.Allocator, io: Io, filepath: []const } pub fn extract(self: *SfsFile, alloc: std.mem.Allocator, io: Io, ext_dir: []const u8, options: ExtractionOptions) !void { - return Extract.extract(alloc, io, self.inode, &self.archive.cache, self.archive.super, ext_dir, options); + return Extract.extractSingleThreaded(alloc, io, self.inode, &self.archive.cache, self.archive.super, ext_dir, options); }