friction is a Zig library for measuring the geometric resistance of memory access patterns.
It quantifies the impedance mismatch between your logical algorithm and the physical memory substrate. It operates on a simple premise supported by modern physics: Mass is not an intrinsic property of data; it is a measure of the resistance it encounters moving through a structure.
In particle physics, geometric torsion may give rise to mass.
In computing, Memory Topology definitely gives rise to latency.
We often treat memory access as a uniform cost. In reality, the "price" of a variable depends on the details of its storage.
- Flat Geometry (Arrays): Data flows linearly. The hardware prefetcher predicts the path perfectly. The data behaves as if it is "massless."
- Twisted Geometry (Pointer Graphs): Data is scattered. The hardware fights against the layout. The data acquires "mass" (latency, heat, cache pollution).
friction allows you to measure this "Twist" directly.
This metric is called Varpi (
Varpi measures the Flow "Triviality" of your data layout. It is the ratio of Physical Displacement to Logical Payload.
Displacement (Bytes Traveled)
ϖ = -----------------------------
Payload (Bytes Processed)
- (Hyper-Efficient): The best code is no code. You reused a register or computed a value procedurally. Zero transport cost.
- (Massless): Ideal linear flow. You moved 1 byte of structure to get 1 byte of payload (e.g., iterating a
u8slice). - (Massive): High structural resistance. You moved 64 bytes (a cache line) to read 4 bytes of payload, or chased a pointer to a cold page.
friction is designed to avoid hidden costs.
It uses explicit allocation and strict resource management.
Use friction.dynamic to instrument hot loops. It tracks the "Bit-Hop" distance of your execution path to calculate the runtime .
const std = @import("std");
const friction = @import("friction");
pub fn main() !void {
// Setup (Explicit Memory)
var gpa = std.heap.GeneralPurposeAllocator(.{}){};
defer _ = gpa.deinit();
// Initialize the tracker.
var tracker = try friction.Tracker(.{ .window_size = 4096 })
.init(gpa.allocator());
defer tracker.deinit();
// Workload
var it = getIterator();
while (it.next()) |item| {
// Log the physical movement of the read head
tracker.track(@intFromPtr(item.ptr), @sizeOf(Item));
process(item);
}
// Report
// Prints the Varpi metric to stderr
tracker.report().format("", .{}, std.io.getStdErr().writer());
}Output:
[ friction report ]
-------------------
Ops: 1,000,000
Payload: 64.00 MB
Displacement: 1.28 GB
Varpi: 20.00x (1.0x = von Neumann ideal)
NOT YET IMPLEMENTED.
Use friction.static to enforce Mechanical Sympathy at compile time. It calculates the intrinsic of a type definition, detecting padding, sparsity, and alignment waste.
const friction = @import("friction");
// A sparse, high-resistance layout
const Node = struct {
active: bool, // 1 byte
// 7 bytes padding (Topological Defect)
id: u64, // 8 bytes
flag: u8, // 1 byte
// 7 bytes padding (Topological Defect)
};
pub fn main() !void {
// This assertion will halt compilation if the layout is too twisted.
// Error: Type 'Node' exceeds varpi threshold! Found: 1.47
comptime try friction.assertVarpi(Node, 1.1);
}Add the dependency to your build.zig.zon:
.{
.name = "my-project",
.version = "0.1.0",
.dependencies = .{
.friction = .{
.url = "https://github.com/wilson/friction/archive/COMMIT.tar.gz",
},
},
}This library adopts a Finitist view of software performance.
Infinite tapes and random-access memory are abstract mathematical fictions. Real machines are finite, physical grids. When you force a machine to navigate a complex geometry (like a linked list or an object graph), you generate heat. friction helps you write code that respects the geometry of the machine.