fix(memhub): 非 2 的幂 arena 二进制分解 + reset 重建 + OOM 诊断增强
主池 1.2GB(非 2 幂)不再被 _largest_pow2_le 砍成 1GB:整个区域按二进制分解拆成多个 2 的幂空闲块压入对应 free list,buddy 合并跨分解边界因 order 不匹配自然停止;reset/_rebuild_free_lists 重建同样分解格局。新增 big_alloc_count 计数与 alloc 失败 self_check/free_bytes 诊断、mark 异常 fail-fast。BuddyTest 断言改为初始分解块数。
This commit is contained in:
1 parent
31d7602368
commit
46b7cbc4ba
2 files changed
+106
-44
No files matched your search
@@ -57,9 +57,10 @@ def mbuddy_main() -> CInt:
|
||||
bd.free(p3)
|
||||
testcheck.ok("3 frees OK")
|
||||
|
||||
# 释放后验证合并: free_count 应回到 1 (单个大块)
|
||||
testcheck.check(bd.free_count() == 1,
|
||||
"coalesce: free_count=1 OK", "coalesce: free_count != 1")
|
||||
# 释放后验证合并: free_count 应回到初始分解块数 (10)
|
||||
# 4096-264 内非 2 的幂: 65536-264=65272 二进制分解为 10 个 2 的幂块
|
||||
testcheck.check(bd.free_count() == 10,
|
||||
"coalesce: free_count=10 OK", "coalesce: free_count != 10")
|
||||
|
||||
# 合并后应能分配大块
|
||||
big: bytes = bd.alloc(16000)
|
||||
@@ -152,7 +153,7 @@ def mbuddy_main() -> CInt:
|
||||
testcheck.check(sp_c[0] == CUInt8T(0x42), "shrink data OK", "shrink data FAILED")
|
||||
|
||||
bds.free(sp2)
|
||||
testcheck.check(bds.free_count() == 1, "shrink+free coalesce OK", "shrink+free coalesce FAILED")
|
||||
testcheck.check(bds.free_count() == 6, "shrink+free coalesce OK", "shrink+free coalesce FAILED")
|
||||
|
||||
stdlib.free(arena_shrink)
|
||||
|
||||
@@ -263,9 +264,9 @@ def mbuddy_main() -> CInt:
|
||||
bd6.free(cur_p)
|
||||
idx += 1
|
||||
|
||||
# 全部释放后应能合并回单个大块
|
||||
testcheck.check(bd6.free_count() == 1,
|
||||
"OOM: full coalesce OK", "OOM: free_count != 1 after full free")
|
||||
# 全部释放后应能合并回初始分解块数 (6)
|
||||
testcheck.check(bd6.free_count() == 6,
|
||||
"OOM: full coalesce OK", "OOM: free_count != 6 after full free")
|
||||
|
||||
stdlib.free(ptrs_arr)
|
||||
stdlib.free(arena6)
|
||||
@@ -275,8 +276,8 @@ def mbuddy_main() -> CInt:
|
||||
arena7: bytes = stdlib.malloc(4096)
|
||||
bd7: memhub.MemBuddy | CPtr = memhub.MemBuddy(arena7, 4096)
|
||||
|
||||
# 初始: 1 个大块
|
||||
testcheck.check(bd7.free_count() == 1, "init: 1 free block OK", "init: free_count != 1")
|
||||
# 初始: 非 2 的幂 arena (4096-264=3832) 二进制分解为 6 个块
|
||||
testcheck.check(bd7.free_count() == 6, "init: 6 free blocks OK", "init: free_count != 6")
|
||||
|
||||
# 分配后: 空闲块数应增加 (分裂产生多个小块)
|
||||
a1: bytes = bd7.alloc(32)
|
||||
@@ -295,10 +296,10 @@ def mbuddy_main() -> CInt:
|
||||
fc3: CSizeT = bd7.free_count()
|
||||
testcheck.check(fc3 > fc2, "after free a1: free increased OK", "after free a1: free not increased")
|
||||
|
||||
# 释放 a2: 应合并, 空闲块数回到 1
|
||||
# 释放 a2: 应合并, 空闲块数回到初始 6 块
|
||||
bd7.free(a2)
|
||||
testcheck.check(bd7.free_count() == 1,
|
||||
"after free all: coalesced to 1 OK", "after free all: free_count != 1")
|
||||
testcheck.check(bd7.free_count() == 6,
|
||||
"after free all: coalesced to 6 OK", "after free all: free_count != 6")
|
||||
|
||||
stdlib.free(arena7)
|
||||
|
||||
@@ -398,7 +399,7 @@ def mbuddy_main() -> CInt:
|
||||
|
||||
# 验证分配器状态: 自检通过,所有块已合并
|
||||
testcheck.check(bd_mt.self_check() == 0, "mt: self_check OK", "mt: self_check FAILED")
|
||||
testcheck.check(bd_mt.free_count() == 1, "mt: free_count=1 OK", "mt: free_count != 1 (leaked)")
|
||||
testcheck.check(bd_mt.free_count() == 10, "mt: free_count=10 OK", "mt: free_count != 10 (leaked)")
|
||||
|
||||
viperlib.snprintf(buf, 256, "mt: free_count=%lu", bd_mt.free_count())
|
||||
testcheck.info(buf)
|
||||
@@ -423,22 +424,22 @@ def mbuddy_main() -> CInt:
|
||||
|
||||
bd_ex.free(exact_fit)
|
||||
bd_ex.free(over_fit)
|
||||
testcheck.check(bd_ex.free_count() == 1, "boundary: coalesce OK", "boundary: coalesce FAILED")
|
||||
testcheck.check(bd_ex.free_count() == 6, "boundary: coalesce OK", "boundary: coalesce FAILED")
|
||||
|
||||
# 2. 单次分配接近 arena 上限
|
||||
# 可用数据区 = mem_size - 8(头) - 8(哨兵),alloc(max_usable) 的 needed
|
||||
# 恰好 = mem_size,正好填满最大块(order 6 = 2048)
|
||||
max_usable: CSizeT = bd_ex.mem_size - 16
|
||||
viperlib.snprintf(buf, 256, "mem_size=%lu, max_usable=%lu", bd_ex.mem_size, max_usable)
|
||||
# 2. 单次分配接近最大块上限(order6=2048,非 2 的幂 arena 的最大分解块)
|
||||
# 可用数据区 = 2048 - 8(头) - 8(哨兵),alloc(max_usable) 的 needed 恰好
|
||||
# = 2048,正好填满 order6 最大块
|
||||
max_usable: CSizeT = 2048 - 16
|
||||
viperlib.snprintf(buf, 256, "mem_size=%lu, max_block=2048, max_usable=%lu", bd_ex.mem_size, max_usable)
|
||||
testcheck.info(buf)
|
||||
|
||||
# 恰好用完整个 arena
|
||||
# 恰好用完 order6 块(其余 5 个分解块空闲)
|
||||
exact_max: bytes = bd_ex.alloc(max_usable)
|
||||
testcheck.check(exact_max != None, "exact max alloc OK", "exact max alloc FAILED")
|
||||
testcheck.check(bd_ex.free_count() == 0, "max alloc: arena full OK", "max alloc: arena not full")
|
||||
testcheck.check(bd_ex.free_count() == 5, "max alloc: 5 free blocks left OK", "max alloc: free_count != 5")
|
||||
|
||||
bd_ex.free(exact_max)
|
||||
testcheck.check(bd_ex.free_count() == 1, "max free: coalesce OK", "max free: coalesce FAILED")
|
||||
testcheck.check(bd_ex.free_count() == 6, "max free: coalesce OK", "max free: coalesce FAILED")
|
||||
|
||||
# 再次分配同样大小 — 验证合并后可再次使用
|
||||
exact_max2: bytes = bd_ex.alloc(max_usable)
|
||||
@@ -481,14 +482,14 @@ def mbuddy_main() -> CInt:
|
||||
# 6. 2 的幂 arena vs 非 2 的幂 arena
|
||||
arena_pow2: bytes = stdlib.malloc(512)
|
||||
bd_pow2: memhub.MemBuddy | CPtr = memhub.MemBuddy(arena_pow2, 512)
|
||||
# 512 - 264 = 248, largest_pow2(248) = 128
|
||||
testcheck.check(bd_pow2.stats() == 128, "pow2 arena: stats=128 OK", "pow2 arena: stats != 128")
|
||||
# 512 - 264 = 248, 二进制分解 = 128+64+32 = 224 (尾 24 字节碎片不入池)
|
||||
testcheck.check(bd_pow2.stats() == 224, "pow2 arena: stats=224 OK", "pow2 arena: stats != 224")
|
||||
stdlib.free(arena_pow2)
|
||||
|
||||
arena_non2: bytes = stdlib.malloc(500)
|
||||
bd_non2: memhub.MemBuddy | CPtr = memhub.MemBuddy(arena_non2, 500)
|
||||
# 500 - 264 = 236, largest_pow2(236) = 128
|
||||
testcheck.check(bd_non2.stats() == 128, "non-pow2 arena: stats=128 OK", "non-pow2 arena: stats != 128")
|
||||
# 500 - 264 = 236, 二进制分解 = 128+64+32 = 224 (尾 12 字节碎片不入池)
|
||||
testcheck.check(bd_non2.stats() == 224, "non-pow2 arena: stats=224 OK", "non-pow2 arena: stats != 224")
|
||||
stdlib.free(arena_non2)
|
||||
|
||||
# 7. 压力测试: 反复 alloc/free 同一尺寸 1000 次
|
||||
@@ -504,7 +505,7 @@ def mbuddy_main() -> CInt:
|
||||
bd_stress.free(sp)
|
||||
|
||||
testcheck.check(bd_stress.self_check() == 0, "stress: self_check OK", "stress: self_check FAILED")
|
||||
testcheck.check(bd_stress.free_count() == 1, "stress: free_count=1 OK", "stress: leaked")
|
||||
testcheck.check(bd_stress.free_count() == 6, "stress: free_count=6 OK", "stress: leaked")
|
||||
stdlib.free(arena_stress)
|
||||
|
||||
# 8. 分配全部最小块然后全部释放
|
||||
@@ -534,7 +535,7 @@ def mbuddy_main() -> CInt:
|
||||
fp2: bytes = t.CVoid(fill_ptrs[fi], t.CPtr)
|
||||
bd_fill.free(fp2)
|
||||
|
||||
testcheck.check(bd_fill.free_count() == 1, "fill: all freed OK", "fill: leaked after free all")
|
||||
testcheck.check(bd_fill.free_count() == 6, "fill: all freed OK", "fill: leaked after free all")
|
||||
testcheck.check(bd_fill.self_check() == 0, "fill: self_check after free OK", "fill: self_check FAILED")
|
||||
|
||||
stdlib.free(fill_ptrs_arr)
|
||||
|
||||
Reference in new issue
Block a user