A couple significant performance improvements for styles (#3097)

To preface, I tested the impact of these changes by running
DOOM-fire-zig in a 120x30 window on a ReleaseFast build. I feel that
DOOM-fire-zig is a fairly realistic target when it comes to "high style
use" terminal applications.

1. By adjusting the conditions under which we swap items while inserting
in to our hash table in `RefCountedSet` we can prioritize styles with
many refs, so they're more likely to stay near their target bucket. This
change brought DFZ FPS from ~700 to ~840, or a roughly 20% improvement.
2. By changing how we hash `Style` structs, we can avoid a lot of
unnecessary overhead and benefit from smaller i-cache size as well as
xxHash's better statistical qualities when compared to Wyhash. This
change brought DFZ FPS from ~840 to ~1020, or another roughly 20%
improvement.

These two changes combined represent as roughly 45% improvement over
`main`, which is... pretty good I'd say.

In addition, I've run `vtebench` and there is no significant difference
in the results between this PR and main.
This commit is contained in:
Mitchell Hashimoto
2024-12-23 19:43:47 -08:00
committed by GitHub
2 changed files with 17 additions and 7 deletions

View File

@ -607,8 +607,16 @@ pub fn RefCountedSet(
break;
}
// This item has a lower PSL, swap it out with our held item.
if (item.meta.psl < held_item.meta.psl) {
// If this item has a lower PSL, or has equal PSL and lower ref
// count, then we swap it out with our held item. By doing this,
// items with high reference counts are prioritized for earlier
// placement. The assumption is that an item which has a higher
// reference count will be accessed more frequently, so we want
// to minimize the time it takes to find it.
if (item.meta.psl < held_item.meta.psl or
item.meta.psl == held_item.meta.psl and
item.meta.ref < held_item.meta.ref)
{
// Put our held item in the bucket.
table[p] = held_id;
held_item.meta.bucket = p;

View File

@ -8,8 +8,7 @@ const Offset = size.Offset;
const OffsetBuf = size.OffsetBuf;
const RefCountedSet = @import("ref_counted_set.zig").RefCountedSet;
const Wyhash = std.hash.Wyhash;
const autoHash = std.hash.autoHash;
const XxHash3 = std.hash.XxHash3;
/// The unique identifier for a style. This is at most the number of cells
/// that can fit into a terminal page.
@ -230,10 +229,13 @@ pub const Style = struct {
_ = try writer.write(" }");
}
/// Hash the raw bytes of the struct with XxHash3
///
/// NOTE: Because the struct does not have a guaranteed in-memory layout
/// this hash is NOT suitable for serialization. If used for a hash
/// table that is then serialized, it MUST be re-hashed when read.
pub fn hash(self: *const Style) u64 {
var hasher = Wyhash.init(0);
autoHash(&hasher, self.*);
return hasher.final();
return XxHash3.hash(0, @as(*const [@sizeOf(Style)]u8, @ptrCast(self)));
}
test {