stage2 大量优化代码使其自举编译时间大幅缩短
This commit is contained in:
1 parent
e02b0c633a
commit
846f441e3a
38 files changed
+2418
-835
No files matched your search
+45
-190
@@ -7,9 +7,14 @@
|
||||
type .{ .Error, .Status, .make } = @import("error.spl");
|
||||
type .{ .Vec } = @import("vec.spl");
|
||||
type .{ .ByteView } = @import("byte_view.spl");
|
||||
type .{ .hash_usize, .eq_usize, .hash_view, .eq_view } = @import("hash.spl");
|
||||
type .{ .checked_add, .checked_mul, .grow_capacity } = @import("memory.spl");
|
||||
type .{ .copy } = @import("../sys/mem.spl");
|
||||
|
||||
#extern(vm) fn vm_map_valid(m: *any): i32;
|
||||
#extern(vm) fn vm_map_reserve(m: *any, need: usize): i32;
|
||||
#extern(vm) fn vm_map_put(m: *any, key: *any, val: *any): i32;
|
||||
#extern(vm) fn vm_map_get(m: *any, key: *any, out: *any): i32;
|
||||
#extern(vm) fn vm_map_del(m: *any, key: *any): i32;
|
||||
#extern(vm) fn vm_map_clear(m: *any): void;
|
||||
#extern(vm) fn vm_map_drop(m: *any): void;
|
||||
|
||||
// 槽位状态:Empty/Occupied/Deleted(墓碑,仅在扩容重哈希时清除)。
|
||||
// 负载因子 70/128。
|
||||
@@ -20,11 +25,6 @@ type SlotState = enum {
|
||||
Deleted,
|
||||
}
|
||||
|
||||
fn state_code(state: SlotState): u8 {
|
||||
var s: SlotState = state;
|
||||
ret @enum2int(s) as u8;
|
||||
}
|
||||
|
||||
// 查找/删除结果:显式区分命中与缺失,替代 bool 哨兵。
|
||||
type LookupResult = enum {
|
||||
Found,
|
||||
@@ -39,11 +39,15 @@ states: Vec,
|
||||
size: usize,
|
||||
key_size: usize,
|
||||
val_size: usize,
|
||||
hash_fn: fn(*u8): usize,
|
||||
eq_fn: fn(*u8, *u8): bool,
|
||||
hash_kind: usize,
|
||||
|
||||
// 键解释:0 = usize;1 = ByteView(按内容哈希/比较)。
|
||||
fn hash_kind_usize(): usize { ret 0; }
|
||||
fn hash_kind_view(): usize { ret 1; }
|
||||
fn hash_kind_constant(): usize { ret 2; }
|
||||
|
||||
fn blank(key_size: usize, key_align: usize, val_size: usize, val_align: usize,
|
||||
hash_fn: fn(*u8): usize, eq_fn: fn(*u8, *u8): bool): Map {
|
||||
hash_kind: usize): Map {
|
||||
ret Map {
|
||||
.keys = Vec.blank(key_size, key_align),
|
||||
.vals = Vec.blank(val_size, val_align),
|
||||
@@ -51,23 +55,30 @@ fn blank(key_size: usize, key_align: usize, val_size: usize, val_align: usize,
|
||||
.size = 0,
|
||||
.key_size = key_size,
|
||||
.val_size = val_size,
|
||||
.hash_fn = hash_fn,
|
||||
.eq_fn = eq_fn,
|
||||
.hash_kind = hash_kind,
|
||||
};
|
||||
}
|
||||
|
||||
fn new(key_size: usize, key_align: usize, val_size: usize, val_align: usize,
|
||||
hash_fn: fn(*u8): usize, eq_fn: fn(*u8, *u8): bool): Map {
|
||||
ret blank(key_size, key_align, val_size, val_align, hash_fn, eq_fn);
|
||||
hash_kind: usize): Map {
|
||||
ret blank(key_size, key_align, val_size, val_align, hash_kind);
|
||||
}
|
||||
|
||||
fn valid(self: Map): bool {
|
||||
if self.key_size == 0 || self.val_size == 0 { ret false; }
|
||||
if self.states.len != self.keys.len || self.states.len != self.vals.len {
|
||||
ret false;
|
||||
}
|
||||
if self.size > self.states.len { ret false; }
|
||||
ret true;
|
||||
fn status_from_code(code: i32): Status {
|
||||
if code == 0 { ret .Ok; }
|
||||
if code == 2 { ret .Err(make(.OutOfRange)); }
|
||||
if code == 3 { ret .Err(make(.Overflow)); }
|
||||
if code == 4 { ret .Err(make(.InvalidAlignment)); }
|
||||
ret .Err(make(.InvalidArgument));
|
||||
}
|
||||
|
||||
fn lookup_from_code(code: i32): LookupResult {
|
||||
if code == 0 { ret .Found; }
|
||||
ret .NotFound;
|
||||
}
|
||||
|
||||
fn valid(self: *Map): bool {
|
||||
ret vm_map_valid(self as *any) != 0;
|
||||
}
|
||||
|
||||
fn size(self: Map): usize {
|
||||
@@ -85,10 +96,8 @@ fn slot_count(self: Map): usize {
|
||||
|
||||
fn slot_state(self: Map, index: usize): SlotState {
|
||||
var code = self.states.data[index];
|
||||
var occupied: SlotState = .Occupied;
|
||||
var deleted_state: SlotState = .Deleted;
|
||||
if code == state_code(occupied) { ret .Occupied; }
|
||||
if code == state_code(deleted_state) { ret .Deleted; }
|
||||
if code == 1 { ret .Occupied; }
|
||||
if code == 2 { ret .Deleted; }
|
||||
ret .Empty;
|
||||
}
|
||||
|
||||
@@ -109,186 +118,32 @@ fn val_at(self: *Map, index: usize): *u8 {
|
||||
ret self.vals.data + index * self.val_size;
|
||||
}
|
||||
|
||||
// 返回占用键的槽位;未找到返回 ~0。容量为 0 时直接未找到。
|
||||
fn find_slot(self: *Map, key: *u8): usize {
|
||||
if self.states.cap == 0 { ret ~(0 as usize); }
|
||||
var mask = self.states.cap - 1;
|
||||
var i = self.hash_fn(key) & mask;
|
||||
var empty: SlotState = .Empty;
|
||||
var occupied: SlotState = .Occupied;
|
||||
var probes: usize = 0;
|
||||
while probes < self.states.cap && self.states.data[i] != state_code(empty) {
|
||||
if self.states.data[i] == state_code(occupied) &&
|
||||
self.eq_fn(self.keys.data + i * self.key_size, key) {
|
||||
ret i;
|
||||
}
|
||||
i = (i + 1) & mask;
|
||||
probes = probes + 1;
|
||||
}
|
||||
ret ~(0 as usize);
|
||||
}
|
||||
|
||||
// 要求容量非零且键不存在。优先复用探测链中的第一个墓碑。
|
||||
fn place(self: *Map, key: *u8, val: *u8) {
|
||||
var mask = self.states.cap - 1;
|
||||
var i = self.hash_fn(key) & mask;
|
||||
var empty: SlotState = .Empty;
|
||||
var deleted: SlotState = .Deleted;
|
||||
var occupied: SlotState = .Occupied;
|
||||
var first_deleted = ~(0 as usize);
|
||||
var probes: usize = 0;
|
||||
while probes < self.states.cap && self.states.data[i] != state_code(empty) {
|
||||
if self.states.data[i] == state_code(deleted) && first_deleted == ~(0 as usize) {
|
||||
first_deleted = i;
|
||||
}
|
||||
i = (i + 1) & mask;
|
||||
probes = probes + 1;
|
||||
}
|
||||
if first_deleted != ~(0 as usize) {
|
||||
i = first_deleted;
|
||||
}
|
||||
copy((self.keys.data + i * self.key_size) as *any, key as *any, self.key_size);
|
||||
copy((self.vals.data + i * self.val_size) as *any, val as *any, self.val_size);
|
||||
self.states.data[i] = state_code(occupied);
|
||||
self.size = self.size + 1;
|
||||
}
|
||||
|
||||
// 保证至少 need 个槽位。所有检查在旧表改动前完成,失败保持原表不变。
|
||||
fn reserve(self: *Map, need: usize): Status {
|
||||
if !valid(self.*) { ret .Err(make(.InvalidArgument)); }
|
||||
if need <= self.states.cap { ret .Ok; }
|
||||
|
||||
var new_cap = grow_capacity(self.states.cap, 8, need);
|
||||
|
||||
var fresh = self.*;
|
||||
fresh.keys = Vec.blank(self.key_size, self.keys.elem_align);
|
||||
fresh.vals = Vec.blank(self.val_size, self.vals.elem_align);
|
||||
fresh.states = Vec.blank(1, 1);
|
||||
fresh.size = 0;
|
||||
|
||||
match Vec.reserve(&fresh.keys, new_cap) {
|
||||
.Ok => {},
|
||||
.Err(error) => { drop(&fresh); ret .Err(error); },
|
||||
}
|
||||
match Vec.reserve(&fresh.vals, new_cap) {
|
||||
.Ok => {},
|
||||
.Err(error) => { drop(&fresh); ret .Err(error); },
|
||||
}
|
||||
match Vec.reserve(&fresh.states, new_cap) {
|
||||
.Ok => {},
|
||||
.Err(error) => { drop(&fresh); ret .Err(error); },
|
||||
}
|
||||
fresh.keys.len = new_cap;
|
||||
fresh.vals.len = new_cap;
|
||||
fresh.states.len = new_cap;
|
||||
|
||||
var i: usize = 0;
|
||||
var empty: SlotState = .Empty;
|
||||
var occupied: SlotState = .Occupied;
|
||||
while i < new_cap {
|
||||
fresh.states.data[i] = state_code(empty);
|
||||
i = i + 1;
|
||||
}
|
||||
|
||||
i = 0;
|
||||
while i < self.states.len {
|
||||
if self.states.data[i] == state_code(occupied) {
|
||||
place(&fresh, self.keys.data + i * self.key_size,
|
||||
self.vals.data + i * self.val_size);
|
||||
}
|
||||
i = i + 1;
|
||||
}
|
||||
|
||||
var old_keys = self.keys;
|
||||
var old_vals = self.vals;
|
||||
var old_states = self.states;
|
||||
self.* = fresh;
|
||||
Vec.drop(&old_keys);
|
||||
Vec.drop(&old_vals);
|
||||
Vec.drop(&old_states);
|
||||
ret .Ok;
|
||||
ret status_from_code(vm_map_reserve(self as *any, need));
|
||||
}
|
||||
|
||||
// 覆盖已存在的键;否则插入并可能扩容。
|
||||
fn put(self: *Map, key: *u8, val: *u8): Status {
|
||||
if !valid(self.*) || key == null || val == null {
|
||||
ret .Err(make(.InvalidArgument));
|
||||
}
|
||||
|
||||
var existing = find_slot(self, key);
|
||||
if existing != ~(0 as usize) {
|
||||
copy((self.vals.data + existing * self.val_size) as *any, val as *any, self.val_size);
|
||||
ret .Ok;
|
||||
}
|
||||
|
||||
if self.states.cap == 0 {
|
||||
match reserve(self, 8) {
|
||||
.Ok => {},
|
||||
.Err(error) => { ret .Err(error); },
|
||||
}
|
||||
} else {
|
||||
var next_size: usize = 0;
|
||||
match checked_add(self.size, 1) {
|
||||
.Ok(value) => { next_size = value; },
|
||||
.Err(error) => { ret .Err(error); },
|
||||
}
|
||||
var scaled: usize = 0;
|
||||
match checked_mul(next_size, 128) {
|
||||
.Ok(value) => { scaled = value; },
|
||||
.Err(error) => { ret .Err(error); },
|
||||
}
|
||||
if scaled / self.states.cap >= 70 {
|
||||
var need: usize = 0;
|
||||
match checked_mul(self.states.cap, 2) {
|
||||
.Ok(value) => { need = value; },
|
||||
.Err(error) => { ret .Err(error); },
|
||||
}
|
||||
match reserve(self, need) {
|
||||
.Ok => {},
|
||||
.Err(error) => { ret .Err(error); },
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
place(self, key, val);
|
||||
ret .Ok;
|
||||
ret status_from_code(vm_map_put(self as *any, key as *any, val as *any));
|
||||
}
|
||||
|
||||
// 找到键则把值复制到 out 并返回 Found;否则 out 不变并返回 NotFound。
|
||||
fn get(self: *Map, key: *u8, out: *u8): LookupResult {
|
||||
if !valid(self.*) || key == null || out == null { ret .NotFound; }
|
||||
var i = find_slot(self, key);
|
||||
if i == ~(0 as usize) { ret .NotFound; }
|
||||
copy(out as *any, (self.vals.data + i * self.val_size) as *any, self.val_size);
|
||||
ret .Found;
|
||||
ret lookup_from_code(vm_map_get(self as *any, key as *any, out as *any));
|
||||
}
|
||||
|
||||
fn del(self: *Map, key: *u8): LookupResult {
|
||||
if !valid(self.*) || key == null { ret .NotFound; }
|
||||
var i = find_slot(self, key);
|
||||
if i == ~(0 as usize) { ret .NotFound; }
|
||||
var deleted_state: SlotState = .Deleted;
|
||||
self.states.data[i] = state_code(deleted_state);
|
||||
self.size = self.size - 1;
|
||||
ret .Found;
|
||||
ret lookup_from_code(vm_map_del(self as *any, key as *any));
|
||||
}
|
||||
|
||||
// 清空内容但保留容量。墓碑与负载计数一并复位。
|
||||
fn clear(self: *Map) {
|
||||
var i: usize = 0;
|
||||
var empty: SlotState = .Empty;
|
||||
while i < self.states.len {
|
||||
self.states.data[i] = state_code(empty);
|
||||
i = i + 1;
|
||||
}
|
||||
self.size = 0;
|
||||
vm_map_clear(self as *any);
|
||||
}
|
||||
|
||||
fn drop(self: *Map) {
|
||||
Vec.drop(&self.keys);
|
||||
Vec.drop(&self.vals);
|
||||
Vec.drop(&self.states);
|
||||
self.size = 0;
|
||||
vm_map_drop(self as *any);
|
||||
}
|
||||
|
||||
fn take(source: *Map): Map {
|
||||
@@ -348,13 +203,13 @@ fn deleted(m: *Map, key: *u8): bool {
|
||||
}
|
||||
|
||||
fn make_int_map(): Map {
|
||||
ret new(@sizeof(usize), @alignof(usize), @sizeof(i32), @alignof(i32), hash_usize, eq_usize);
|
||||
ret new(@sizeof(usize), @alignof(usize), @sizeof(i32), @alignof(i32), hash_kind_usize());
|
||||
}
|
||||
|
||||
fn test_put_get_update() {
|
||||
var m = make_int_map();
|
||||
@assert(size(m) == 0 && capacity(m) == 0, "empty map");
|
||||
@assert(valid(m));
|
||||
@assert(valid(&m));
|
||||
|
||||
var key: usize = 0;
|
||||
var value: i32 = 0;
|
||||
@@ -367,7 +222,7 @@ fn test_put_get_update() {
|
||||
key = 7;
|
||||
value = 70;
|
||||
expect_ok(put(&m, &key as *u8, &value as *u8));
|
||||
@assert(size(m) == 1 && valid(m));
|
||||
@assert(size(m) == 1 && valid(&m));
|
||||
|
||||
out = -1;
|
||||
@assert(found(&m, &key as *u8, &out as *u8) && out == 70, "insert then get");
|
||||
@@ -463,7 +318,7 @@ fn test_take_and_self_put() {
|
||||
|
||||
fn test_byte_view_keys() {
|
||||
var m = new(@sizeof(ByteView), @alignof(ByteView), @sizeof(i32), @alignof(i32),
|
||||
hash_view, eq_view);
|
||||
hash_kind_view());
|
||||
defer { drop(&m); }
|
||||
|
||||
var alpha = ByteView.from_slice("alpha");
|
||||
|
||||
Reference in new issue
Block a user