Skip to content
Open
Show file tree
Hide file tree
Changes from 1 commit
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
96 changes: 96 additions & 0 deletions bench/bench.zig
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,93 @@ fn printResults(writer: anytype, label: []const u8, iterations: usize, elapsed_n
);
}

fn asciiPrintableRunLen(input: []const u8) usize {
const VecLenOpt = std.simd.suggestVectorLength(u8);
if (VecLenOpt) |VecLen| {
const Vec = @Vector(VecLen, u8);
const lo: Vec = @splat(0x20);
const hi: Vec = @splat(0x7E);
var i: usize = 0;
while (i + VecLen <= input.len) : (i += VecLen) {
const chunk = @as(*const [VecLen]u8, @ptrCast(input[i..].ptr)).*;
const vec: Vec = chunk;
const ok = (vec >= lo) & (vec <= hi);
if (!@reduce(.And, ok)) {
var j: usize = 0;
while (j < VecLen) : (j += 1) {
const b = input[i + j];
if (b < 0x20 or b > 0x7E) return i + j;
}
}
}
while (i < input.len) : (i += 1) {
const b = input[i];
if (b < 0x20 or b > 0x7E) return i;
}
return input.len;
}

var i: usize = 0;
while (i < input.len) : (i += 1) {
const b = input[i];
if (b < 0x20 or b > 0x7E) return i;
}
return input.len;
}

fn benchParseStreamBaseline(writer: anytype, label: []const u8, parser: *vaxis.Parser, input: []const u8, iterations: usize) !void {
var timer = try std.time.Timer.start();
var i: usize = 0;
while (i < iterations) : (i += 1) {
var idx: usize = 0;
while (idx < input.len) {
const result = try parser.parse(input[idx..], null);
if (result.n == 0) break;
idx += result.n;
std.mem.doNotOptimizeAway(result);
}
std.mem.doNotOptimizeAway(idx);
}
const elapsed_ns = timer.read();
try printResults(writer, label, iterations, elapsed_ns, input.len * iterations);
}

fn benchParseStreamSimd(writer: anytype, label: []const u8, parser: *vaxis.Parser, input: []const u8, iterations: usize) !void {
var timer = try std.time.Timer.start();
var i: usize = 0;
while (i < iterations) : (i += 1) {
var idx: usize = 0;
while (idx < input.len) {
const slice = input[idx..];
var ascii_len = asciiPrintableRunLen(slice);
if (ascii_len > 0 and ascii_len < slice.len and slice[ascii_len] >= 0x80) {
ascii_len -= 1;
}
if (ascii_len > 0) {
var j: usize = 0;
while (j < ascii_len) : (j += 1) {
const key: vaxis.Key = .{
.codepoint = slice[j],
.text = slice[j .. j + 1],
};
const event: vaxis.Event = .{ .key_press = key };
std.mem.doNotOptimizeAway(event);
}
idx += ascii_len;
continue;
}

const result = try parser.parse(slice, null);
if (result.n == 0) break;
idx += result.n;
std.mem.doNotOptimizeAway(result);
}
std.mem.doNotOptimizeAway(idx);
}
const elapsed_ns = timer.read();
try printResults(writer, label, iterations, elapsed_ns, input.len * iterations);
}

pub fn main() !void {
var gpa = std.heap.GeneralPurposeAllocator(.{}){};
defer _ = gpa.deinit();
Expand Down Expand Up @@ -59,4 +146,13 @@ pub fn main() !void {
const dirty_ns = timer.read();
const dirty_bytes: usize = dirty_writer.writer.end;
try printResults(stdout, "dirty", iterations, dirty_ns, dirty_bytes);

var parser: vaxis.Parser = .{};
const mixed_stream = "The quick brown fox jumps over the lazy dog " ++
"1234567890 !@#$%^&*() " ++
"\x1b[A" ++
"世界 1️⃣ 👩‍🚀!" ++
"\r";
try benchParseStreamBaseline(stdout, "parse_stream_loop_baseline", &parser, mixed_stream, iterations);
try benchParseStreamSimd(stdout, "parse_stream_loop_simd", &parser, mixed_stream, iterations);
}
55 changes: 55 additions & 0 deletions src/Loop.zig
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,40 @@ const Vaxis = @import("Vaxis.zig");

const log = std.log.scoped(.vaxis);

fn asciiPrintableRunLen(input: []const u8) usize {
const VecLenOpt = std.simd.suggestVectorLength(u8);
if (VecLenOpt) |VecLen| {
const Vec = @Vector(VecLen, u8);
const lo: Vec = @splat(0x20);
const hi: Vec = @splat(0x7E);
var i: usize = 0;
while (i + VecLen <= input.len) : (i += VecLen) {
const chunk = @as(*const [VecLen]u8, @ptrCast(input[i..].ptr)).*;
const vec: Vec = chunk;
const ok = (vec >= lo) & (vec <= hi);
if (!@reduce(.And, ok)) {
var j: usize = 0;
while (j < VecLen) : (j += 1) {
const b = input[i + j];
if (b < 0x20 or b > 0x7E) return i + j;
}
}
}
while (i < input.len) : (i += 1) {
const b = input[i];
if (b < 0x20 or b > 0x7E) return i;
}
return input.len;
}

var i: usize = 0;
while (i < input.len) : (i += 1) {
const b = input[i];
if (b < 0x20 or b > 0x7E) return i;
}
return input.len;
}

pub fn Loop(comptime T: type) type {
return struct {
const Self = @This();
Expand Down Expand Up @@ -137,6 +171,27 @@ pub fn Loop(comptime T: type) type {
const n = try self.tty.read(buf[read_start..]);
var seq_start: usize = 0;
while (seq_start < n) {
if (@hasField(Event, "key_press")) {
const input = buf[seq_start..n];
var ascii_len = asciiPrintableRunLen(input);
if (ascii_len > 0 and ascii_len < input.len and input[ascii_len] >= 0x80) {
ascii_len -= 1;
}
if (ascii_len > 0) {
var i: usize = 0;
while (i < ascii_len) : (i += 1) {
const key: vaxis.Key = .{
.codepoint = input[i],
.text = input[i .. i + 1],
};
const event: Event = .{ .key_press = key };
try handleEventGeneric(self, self.vaxis, &cache, Event, event, paste_allocator);
}
read_start = 0;
seq_start += ascii_len;
continue;
}
}
const result = try parser.parse(buf[seq_start..n], paste_allocator);
if (result.n == 0) {
// copy the read to the beginning. We don't use memcpy because
Expand Down