Add README and simplify ViewEngine API
- ViewEngine.init() no longer requires allocator - render() and renderTpl() accept allocator parameter - Remove deinit() - no resources to clean up - Remove unused parse/renderDoc methods - Add memory management guidance to runtime.zig - Clean up unused imports and options
This commit is contained in:
170
src/benchmarks/profile_friends.zig
Normal file
170
src/benchmarks/profile_friends.zig
Normal file
@@ -0,0 +1,170 @@
|
||||
const std = @import("std");
|
||||
const pugz = @import("pugz");
|
||||
|
||||
const friends_tpl =
|
||||
\\doctype html
|
||||
\\html(lang="en")
|
||||
\\ head
|
||||
\\ meta(charset="UTF-8")
|
||||
\\ title Friends
|
||||
\\ body
|
||||
\\ div.friends
|
||||
\\ each friend in friends
|
||||
\\ div.friend
|
||||
\\ ul
|
||||
\\ li Name: #{friend.name}
|
||||
\\ li Balance: #{friend.balance}
|
||||
\\ li Age: #{friend.age}
|
||||
\\ li Address: #{friend.address}
|
||||
\\ li Image:
|
||||
\\ img(src=friend.picture)
|
||||
\\ li Company: #{friend.company}
|
||||
\\ li Email:
|
||||
\\ a(href=friend.emailHref) #{friend.email}
|
||||
\\ li About: #{friend.about}
|
||||
\\ if friend.tags
|
||||
\\ li Tags:
|
||||
\\ ul
|
||||
\\ each tag in friend.tags
|
||||
\\ li #{tag}
|
||||
\\ if friend.friends
|
||||
\\ li Friends:
|
||||
\\ ul
|
||||
\\ each subFriend in friend.friends
|
||||
\\ li #{subFriend.name} (#{subFriend.id})
|
||||
;
|
||||
|
||||
const SubFriend = struct { id: i32, name: []const u8 };
|
||||
const Friend = struct {
|
||||
name: []const u8,
|
||||
balance: []const u8,
|
||||
age: i32,
|
||||
address: []const u8,
|
||||
picture: []const u8,
|
||||
company: []const u8,
|
||||
email: []const u8,
|
||||
emailHref: []const u8,
|
||||
about: []const u8,
|
||||
tags: ?[]const []const u8,
|
||||
friends: ?[]const SubFriend,
|
||||
};
|
||||
|
||||
pub fn main() !void {
|
||||
var gpa = std.heap.GeneralPurposeAllocator(.{}){};
|
||||
defer _ = gpa.deinit();
|
||||
|
||||
var arena = std.heap.ArenaAllocator.init(gpa.allocator());
|
||||
defer arena.deinit();
|
||||
|
||||
const engine = pugz.ViewEngine.init(.{});
|
||||
|
||||
const friend_tags = &[_][]const u8{ "id", "amet", "non", "ut", "dolore", "commodo", "consequat" };
|
||||
const sub_friends = &[_]SubFriend{
|
||||
.{ .id = 0, .name = "Gates Lewis" },
|
||||
.{ .id = 1, .name = "Britt Stokes" },
|
||||
.{ .id = 2, .name = "Reed Wade" },
|
||||
};
|
||||
|
||||
var friends_data: [100]Friend = undefined;
|
||||
for (&friends_data, 0..) |*f, i| {
|
||||
f.* = .{
|
||||
.name = "Gardner Alvarez",
|
||||
.balance = "$1,509.00",
|
||||
.age = 30 + @as(i32, @intCast(i % 20)),
|
||||
.address = "282 Lancaster Avenue, Bowden, Kansas, 666",
|
||||
.picture = "http://placehold.it/32x32",
|
||||
.company = "Dentrex",
|
||||
.email = "gardneralvarez@dentrex.com",
|
||||
.emailHref = "mailto:gardneralvarez@dentrex.com",
|
||||
.about = "Minim elit tempor enim voluptate labore do non nisi sint nulla deserunt officia proident excepteur.",
|
||||
.tags = friend_tags,
|
||||
.friends = sub_friends,
|
||||
};
|
||||
}
|
||||
|
||||
const data = .{ .friends = &friends_data };
|
||||
|
||||
// Warmup
|
||||
for (0..10) |_| {
|
||||
_ = arena.reset(.retain_capacity);
|
||||
_ = try engine.renderTpl(arena.allocator(), friends_tpl, data);
|
||||
}
|
||||
|
||||
// Get output size
|
||||
_ = arena.reset(.retain_capacity);
|
||||
const output = try engine.renderTpl(arena.allocator(), friends_tpl, data);
|
||||
const output_size = output.len;
|
||||
|
||||
// Profile render
|
||||
const iterations: usize = 500;
|
||||
var total_render: u64 = 0;
|
||||
var timer = try std.time.Timer.start();
|
||||
|
||||
for (0..iterations) |_| {
|
||||
_ = arena.reset(.retain_capacity);
|
||||
timer.reset();
|
||||
_ = try engine.renderTpl(arena.allocator(), friends_tpl, data);
|
||||
total_render += timer.read();
|
||||
}
|
||||
|
||||
const avg_render_us = @as(f64, @floatFromInt(total_render)) / @as(f64, @floatFromInt(iterations)) / 1000.0;
|
||||
const total_ms = @as(f64, @floatFromInt(total_render)) / 1_000_000.0;
|
||||
|
||||
// Header
|
||||
std.debug.print("\n", .{});
|
||||
std.debug.print("╔══════════════════════════════════════════════════════════════╗\n", .{});
|
||||
std.debug.print("║ FRIENDS TEMPLATE CPU PROFILE ║\n", .{});
|
||||
std.debug.print("╠══════════════════════════════════════════════════════════════╣\n", .{});
|
||||
std.debug.print("║ Iterations: {d:<6} Output size: {d:<6} bytes ║\n", .{ iterations, output_size });
|
||||
std.debug.print("╚══════════════════════════════════════════════════════════════╝\n\n", .{});
|
||||
|
||||
// Results
|
||||
std.debug.print("┌────────────────────────────────────┬─────────────────────────┐\n", .{});
|
||||
std.debug.print("│ Metric │ Value │\n", .{});
|
||||
std.debug.print("├────────────────────────────────────┼─────────────────────────┤\n", .{});
|
||||
std.debug.print("│ Total time │ {d:>10.1} ms │\n", .{total_ms});
|
||||
std.debug.print("│ Avg per render │ {d:>10.1} µs │\n", .{avg_render_us});
|
||||
std.debug.print("│ Renders/sec │ {d:>10.0} │\n", .{1_000_000.0 / avg_render_us});
|
||||
std.debug.print("└────────────────────────────────────┴─────────────────────────┘\n", .{});
|
||||
|
||||
// Template complexity breakdown
|
||||
std.debug.print("\n📋 Template Complexity:\n", .{});
|
||||
std.debug.print(" • 100 friends (outer loop)\n", .{});
|
||||
std.debug.print(" • 7 tags per friend (nested loop) = 700 tag iterations\n", .{});
|
||||
std.debug.print(" • 3 sub-friends per friend (nested loop) = 300 sub-friend iterations\n", .{});
|
||||
std.debug.print(" • Total loop iterations: 100 + 700 + 300 = 1,100\n", .{});
|
||||
std.debug.print(" • ~10 interpolations per friend = 1,000+ variable lookups\n", .{});
|
||||
std.debug.print(" • 2 conditionals per friend = 200 conditional evaluations\n", .{});
|
||||
|
||||
// Cost breakdown estimate
|
||||
const loop_iterations: f64 = 1100;
|
||||
const var_lookups: f64 = 1500; // approximate
|
||||
|
||||
std.debug.print("\n💡 Estimated Cost Breakdown (per render):\n", .{});
|
||||
std.debug.print(" Total: {d:.1} µs\n", .{avg_render_us});
|
||||
std.debug.print(" Per loop iteration: ~{d:.2} µs ({d:.0} iterations)\n", .{ avg_render_us / loop_iterations, loop_iterations });
|
||||
std.debug.print(" Per variable lookup: ~{d:.3} µs ({d:.0} lookups)\n", .{ avg_render_us / var_lookups, var_lookups });
|
||||
|
||||
// Comparison
|
||||
std.debug.print("\n📊 Comparison with Pug.js:\n", .{});
|
||||
const pugjs_us: f64 = 55.0; // From benchmark: 110ms / 2000 = 55µs
|
||||
std.debug.print(" Pug.js: {d:.1} µs/render\n", .{pugjs_us});
|
||||
std.debug.print(" Pugz: {d:.1} µs/render\n", .{avg_render_us});
|
||||
const ratio = avg_render_us / pugjs_us;
|
||||
if (ratio > 1.0) {
|
||||
std.debug.print(" Status: Pugz is {d:.1}x SLOWER\n", .{ratio});
|
||||
} else {
|
||||
std.debug.print(" Status: Pugz is {d:.1}x FASTER\n", .{1.0 / ratio});
|
||||
}
|
||||
|
||||
std.debug.print("\nKey Bottlenecks (likely):\n", .{});
|
||||
std.debug.print(" 1. Data conversion: Zig struct -> pugz.Value (comptime reflection)\n", .{});
|
||||
std.debug.print(" 2. Variable lookup: HashMap get() for each interpolation\n", .{});
|
||||
std.debug.print(" 3. AST traversal: Walking tree nodes vs Pug.js compiled JS functions\n", .{});
|
||||
std.debug.print(" 4. Loop scope: Creating/clearing scope per loop iteration\n", .{});
|
||||
|
||||
std.debug.print("\nAlready optimized:\n", .{});
|
||||
std.debug.print(" - Scope pooling (reuse hashmap capacity)\n", .{});
|
||||
std.debug.print(" - Batched HTML escaping\n", .{});
|
||||
std.debug.print(" - Arena allocator with retain_capacity\n", .{});
|
||||
}
|
||||
Reference in New Issue
Block a user