stage2 提供近乎完整的功能但出现严重性能问题 添加stage0的原生性能记录 stage1添加更多内置函数 nob重构代码使其更加健壮

This commit is contained in:
zzy committed 2026-09-19 18:08:43 +08:00
1 parent b5a7a811b3
commit e02b0c633a
35 files changed
+14018 -216

No files matched your search

+303
View File
@@ -0,0 +1,303 @@
// Expect retcode = 0
// 全量热点/耗时记录器(配合 @file_name()/@line()/@col() 与 sys/time.spl)。
// 每个调用点聚合:count、独占(self)与包含(total)的 steps/ns。
// - total_* 含被调用者(嵌套会重复,跨站点求和会超过墙钟)
// - self_* = total_* - 子区间之和,用于定位真正的热点
// 用法(严格 LIFO,defer 正好满足):
// var tok = Profile.begin(&prof, @file_name(), @line(), @col());
// defer { Profile.end(&prof, tok); }
type .{ .Error } = @import("error.spl");
type .{ .Vec } = @import("vec.spl");
type .{ .ByteView } = @import("byte_view.spl");
type .{ .ByteBuffer } = @import("byte_buffer.spl");
type .{ .append_usize } = @import("fmt.spl");
type .{ .open_cstr, .write_all } = @import("io.spl");
type File = @import("../sys/file.spl");
type Time = @import("../sys/time.spl");
type Site = struct {
fname: ByteView,
line: usize,
col: usize,
count: usize,
self_steps: usize,
total_steps: usize,
self_ns: usize,
total_ns: usize,
}
type SpanRec = struct {
site: usize,
t0_ns: usize,
t0_steps: usize,
child_steps: usize,
child_ns: usize,
active: bool,
}
type Profile = @self;
sites: Vec, // Site
spans: Vec, // SpanRec
stack: Vec, // usize(活动 span 的 token,LIFO)
has_time: bool,
fn new(): Profile {
var has = false;
if Time.available() != 0 { has = true; }
ret Profile {
.sites = Vec.blank(@sizeof(Site), @alignof(Site)),
.spans = Vec.blank(@sizeof(SpanRec), @alignof(SpanRec)),
.stack = Vec.blank(@sizeof(usize), @alignof(usize)),
.has_time = has,
};
}
fn drop(self: *Profile) {
Vec.drop(&self.stack);
Vec.drop(&self.spans);
Vec.drop(&self.sites);
}
fn site_at(self: *Profile, i: usize): *Site {
match Vec.get(&self.sites, i) {
.Ok(ptr) => { ret ptr as *Site; },
.Err(error) => { ret null; },
}
ret null;
}
fn span_at(self: *Profile, i: usize): *SpanRec {
match Vec.get(&self.spans, i) {
.Ok(ptr) => { ret ptr as *SpanRec; },
.Err(error) => { ret null; },
}
ret null;
}
fn usize_at(v: *Vec, i: usize): usize {
match Vec.get(v, i) {
.Ok(ptr) => { ret (ptr as *usize).*; },
.Err(error) => { ret 0; },
}
ret 0;
}
fn find_site(self: *Profile, fname: []u8, line: usize, col: usize): usize {
var fv = ByteView.from_slice(fname);
var i: usize = 0;
while i < self.sites.len {
var s = site_at(self, i);
if s != null && s.line == line && s.col == col && ByteView.eq(s.fname, fv) {
ret i;
}
i = i + 1;
}
var fresh = Site {
.fname = fv,
.line = line,
.col = col,
.count = 0,
.self_steps = 0,
.total_steps = 0,
.self_ns = 0,
.total_ns = 0,
};
match Vec.push(&self.sites, &fresh as *u8) {
.Ok => {},
.Err(error) => { ret 0; },
}
ret self.sites.len - 1;
}
// 仅计数(轻量点)。
fn hit(self: *Profile, fname: []u8, line: usize, col: usize) {
var site = find_site(self, fname, line, col);
var s = site_at(self, site);
if s != null { s.count = s.count + 1; }
}
fn begin(self: *Profile, fname: []u8, line: usize, col: usize): usize {
var site = find_site(self, fname, line, col);
var s = site_at(self, site);
if s != null { s.count = s.count + 1; }
var t_ns: usize = 0;
if self.has_time { t_ns = Time.now_ns(); }
var rec = SpanRec {
.site = site,
.t0_ns = t_ns,
.t0_steps = Time.steps(),
.child_steps = 0,
.child_ns = 0,
.active = true,
};
match Vec.push(&self.spans, &rec as *u8) {
.Ok => {},
.Err(error) => { ret 0; },
}
var token = self.spans.len - 1;
match Vec.push(&self.stack, &token as *u8) { .Ok => {}, .Err(error) => {} }
ret token;
}
fn end(self: *Profile, token: usize) {
var rec = span_at(self, token);
if rec == null || !rec.active { ret; }
var e_steps = Time.steps() - rec.t0_steps;
var e_ns: usize = 0;
if self.has_time { e_ns = Time.now_ns() - rec.t0_ns; }
var s = site_at(self, rec.site);
if s != null {
s.total_steps = s.total_steps + e_steps;
s.self_steps = s.self_steps + (e_steps - rec.child_steps);
if self.has_time {
s.total_ns = s.total_ns + e_ns;
s.self_ns = s.self_ns + (e_ns - rec.child_ns);
}
}
rec.active = false;
// 弹栈并把区间计入父 span(LIFO)。
var popped: usize = 0;
match Vec.pop(&self.stack, &popped as *u8) { .Ok => {}, .Err(error) => {} }
var n = self.stack.len;
if n > 0 {
var p = span_at(self, usize_at(&self.stack, n - 1));
if p != null {
p.child_steps = p.child_steps + e_steps;
p.child_ns = p.child_ns + e_ns;
}
}
}
// ---- dump / save ----
fn out_s(out: *ByteBuffer, s: []u8): bool {
match ByteBuffer.append(out, s) { .Ok => { ret true; }, .Err(error) => { ret false; } }
ret false;
}
fn out_u(out: *ByteBuffer, v: usize): bool {
match append_usize(out, v, 10, false) { .Ok => { ret true; }, .Err(error) => { ret false; } }
ret false;
}
// 列:count self_steps total_steps self_ns total_ns avg_self location;按 self_steps 降序。
fn dump(self: *Profile, out: *ByteBuffer): bool {
if !out_s(out, "profile sites=") { ret false; }
if !out_u(out, self.sites.len) { ret false; }
if !out_s(out, " has_time=") { ret false; }
if !out_u(out, if self.has_time { 1 } else { 0 }) { ret false; }
if !out_s(out, "\n") { ret false; }
if !out_s(out, "count\tself_steps\ttotal_steps\tself_ns\ttotal_ns\tavg_self\tlocation\n") {
ret false;
}
var idx = Vec.blank(@sizeof(usize), @alignof(usize));
defer { Vec.drop(&idx); }
var i: usize = 0;
while i < self.sites.len {
match Vec.push(&idx, &i as *u8) { .Ok => {}, .Err(error) => {} }
i = i + 1;
}
i = 1;
while i < idx.len {
var j = i;
while j > 0 {
var a = site_at(self, usize_at(&idx, j - 1));
var b = site_at(self, usize_at(&idx, j));
var swap = false;
if a != null && b != null {
if b.self_steps > a.self_steps { swap = true; }
}
if !swap { break; }
var tmp = usize_at(&idx, j - 1);
match Vec.get(&idx, j - 1) { .Ok(p) => { (p as *usize).* = usize_at(&idx, j); }, .Err(e) => {} }
match Vec.get(&idx, j) { .Ok(p) => { (p as *usize).* = tmp; }, .Err(e) => {} }
j = j - 1;
}
i = i + 1;
}
i = 0;
while i < idx.len {
var s = site_at(self, usize_at(&idx, i));
if s != null {
if !out_u(out, s.count) { ret false; }
if !out_s(out, "\t") { ret false; }
if !out_u(out, s.self_steps) { ret false; }
if !out_s(out, "\t") { ret false; }
if !out_u(out, s.total_steps) { ret false; }
if !out_s(out, "\t") { ret false; }
if !out_u(out, s.self_ns) { ret false; }
if !out_s(out, "\t") { ret false; }
if !out_u(out, s.total_ns) { ret false; }
if !out_s(out, "\t") { ret false; }
if s.count != 0 { if !out_u(out, s.self_steps / s.count) { ret false; } }
else { if !out_s(out, "0") { ret false; } }
if !out_s(out, "\t") { ret false; }
if !out_s(out, ByteView.as_slice(s.fname)) { ret false; }
if !out_s(out, ":") { ret false; }
if !out_u(out, s.line) { ret false; }
if !out_s(out, ":") { ret false; }
if !out_u(out, s.col) { ret false; }
if !out_s(out, "\n") { ret false; }
}
i = i + 1;
}
ret true;
}
fn save(self: *Profile, path: ByteView): bool {
var out = ByteBuffer.new();
defer { ByteBuffer.drop(&out); }
if !dump(self, &out) { ret false; }
var f = open_cstr(path, ByteView.from_slice("wb"));
if !File.valid(&f) { ret false; }
var w = write_all(&f, ByteBuffer.as_slice(out));
var closed = File.close(&f);
match w {
.Ok => {},
.Err(error) => { ret false; },
}
if closed != 0 { ret false; }
ret true;
}
// ---- 测试 ----
fn spin(n: usize): usize {
var i: usize = 0;
var s: usize = 0;
while i < n { s = s + i; i = i + 1; }
ret s;
}
fn test_profile() {
var p = new();
defer { drop(&p); }
var tok = begin(&p, @file_name(), @line(), @col());
var v = spin(1000);
end(&p, tok);
@assert(p.sites.len >= 1, "sites");
var s = site_at(&p, 0);
@assert(s != null && s.count >= 1, "count");
@assert(s.total_steps > 0, "steps");
@assert(s.self_steps > 0, "self steps");
if v == 0 { ignore_usize(v + 1); }
}
fn ignore_usize(v: usize) {}
fn main() {
test_profile();
}