Skip to content

Commit db20b5b

Browse files
committed
WIP
1 parent e44741d commit db20b5b

File tree

13 files changed

+4056
-28
lines changed

13 files changed

+4056
-28
lines changed

docs/bitmap_coalesce.md

Lines changed: 579 additions & 0 deletions
Large diffs are not rendered by default.

prototype/index_table.py

Lines changed: 240 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,240 @@
1+
#!/usr/bin/env python3
2+
"""
3+
Exhaustively test all blocks in a 256-unit arena.
4+
For each block (address, size), determine which size classes it can serve
5+
with natural alignment. Group blocks by their unique servable set.
6+
"""
7+
8+
ARENA = 256
9+
B = 2 # intermediate bits
10+
11+
def gen_size_classes(max_size):
12+
"""Generate all valid size classes up to max_size with B=2."""
13+
classes = set()
14+
# e=0: S = 1
15+
# e=1: S = 2, 3
16+
# e>=2: S = 2^e + m * 2^(e-2) for m in 0..3
17+
classes.add(1)
18+
classes.add(2)
19+
classes.add(3)
20+
e = 2
21+
while True:
22+
base = 1 << e
23+
step = 1 << (e - B)
24+
for m in range(1 << B):
25+
s = base + m * step
26+
if s > max_size:
27+
break
28+
classes.add(s)
29+
if base > max_size:
30+
break
31+
e += 1
32+
return sorted(classes)
33+
34+
def align(x):
35+
"""Natural alignment: largest power of 2 dividing x. align(0) = infinity."""
36+
if x == 0:
37+
return 1 << 30 # effectively infinite
38+
return x & (-x)
39+
40+
def can_serve(addr, block_size, sizeclass):
41+
"""Can block [addr, addr+block_size) serve a naturally-aligned allocation of sizeclass?"""
42+
A = align(sizeclass)
43+
# First A-aligned address >= addr
44+
first_aligned = ((addr + A - 1) // A) * A
45+
# Need first_aligned + sizeclass <= addr + block_size
46+
return first_aligned + sizeclass <= addr + block_size
47+
48+
def main():
49+
size_classes = gen_size_classes(ARENA)
50+
print(f"Size classes (B={B}, up to {ARENA}): {size_classes}")
51+
print(f"Count: {len(size_classes)}")
52+
print()
53+
54+
# For each (addr, block_size), compute the set of servable size classes
55+
# Key: frozenset of servable classes -> list of (addr, size) blocks
56+
groups = {}
57+
58+
for a in range(ARENA):
59+
for n in range(1, ARENA - a + 1):
60+
servable = frozenset(
61+
sc for sc in size_classes if can_serve(a, n, sc)
62+
)
63+
if servable not in groups:
64+
groups[servable] = []
65+
groups[servable].append((a, n))
66+
67+
# Sort groups by the minimum block size that achieves this set
68+
sorted_groups = sorted(groups.items(), key=lambda kv: (len(kv[0]), min(kv[0]) if kv[0] else 0))
69+
70+
print(f"Total unique servable sets: {len(groups)}")
71+
print()
72+
73+
# Now the key question: does the servable set depend only on (block_size, align(addr))?
74+
# Check this empirically
75+
print("=" * 80)
76+
print("CHECKING: does servable set depend only on (block_size, align(addr))?")
77+
print("=" * 80)
78+
79+
by_size_align = {}
80+
conflicts = 0
81+
for a in range(ARENA):
82+
for n in range(1, ARENA - a + 1):
83+
servable = frozenset(
84+
sc for sc in size_classes if can_serve(a, n, sc)
85+
)
86+
alpha = min(align(a), ARENA) # cap alignment
87+
key = (n, alpha)
88+
if key not in by_size_align:
89+
by_size_align[key] = servable
90+
elif by_size_align[key] != servable:
91+
conflicts += 1
92+
if conflicts <= 5:
93+
print(f" CONFLICT: (size={n}, align={alpha})")
94+
print(f" existing: {sorted(by_size_align[key])}")
95+
print(f" new (a={a}): {sorted(servable)}")
96+
97+
if conflicts == 0:
98+
print(" NO CONFLICTS! Servable set depends only on (block_size, align(addr)).")
99+
else:
100+
print(f" {conflicts} total conflicts found.")
101+
print()
102+
103+
# Now build the flattened index: unique sets indexed by (block_size, align(addr))
104+
# Group by unique servable set, showing which (size, align) pairs map to it
105+
print("=" * 80)
106+
print("FLATTENED INDEX: unique servable sets ordered by inclusion")
107+
print("=" * 80)
108+
109+
# Collect unique sets from the (size, align) perspective
110+
unique_sets = {}
111+
for (n, alpha), servable in sorted(by_size_align.items()):
112+
if servable not in unique_sets:
113+
unique_sets[servable] = []
114+
unique_sets[servable].append((n, alpha))
115+
116+
# Sort by set size then min element
117+
sorted_sets = sorted(unique_sets.items(),
118+
key=lambda kv: (len(kv[0]), max(kv[0]) if kv[0] else 0))
119+
120+
for idx, (servable, pairs) in enumerate(sorted_sets):
121+
sc_list = sorted(servable)
122+
# Find the minimum block size across all (n, alpha) pairs
123+
min_n = min(n for n, _ in pairs)
124+
max_n = max(n for n, _ in pairs)
125+
# Show a compact representation of which (size, align) pairs give this set
126+
print(f"\nIndex {idx}: servable = {sc_list}")
127+
print(f" |servable| = {len(servable)}, block sizes [{min_n}..{max_n}]")
128+
# Show a few representative (n, alpha) pairs
129+
pairs_sorted = sorted(pairs)
130+
if len(pairs_sorted) <= 10:
131+
for n, alpha in pairs_sorted:
132+
print(f" (size={n}, align={alpha})")
133+
else:
134+
for n, alpha in pairs_sorted[:5]:
135+
print(f" (size={n}, align={alpha})")
136+
print(f" ... ({len(pairs_sorted)} total pairs)")
137+
for n, alpha in pairs_sorted[-3:]:
138+
print(f" (size={n}, align={alpha})")
139+
140+
print()
141+
print("=" * 80)
142+
print(f"SUMMARY: {len(unique_sets)} unique servable sets (= flat index entries)")
143+
print("=" * 80)
144+
145+
# Now show just the index by (block_size, block_align) -> index
146+
print()
147+
print("=" * 80)
148+
print("INDEX TABLE: (block_size, block_align) -> index")
149+
print("Showing for block sizes 1..64 and aligns 1,2,4,8,16,32,64")
150+
print("=" * 80)
151+
152+
# Assign index numbers
153+
set_to_idx = {}
154+
for idx, (servable, _) in enumerate(sorted_sets):
155+
set_to_idx[servable] = idx
156+
157+
# Print header
158+
aligns = [1, 2, 4, 8, 16, 32, 64, 128, 256]
159+
print(f"{'size':>6}", end="")
160+
for alpha in aligns:
161+
print(f" α={alpha:>3}", end="")
162+
print(" servable max (at high α)")
163+
164+
for n in range(1, 65):
165+
print(f"{n:>6}", end="")
166+
for alpha in aligns:
167+
key = (n, alpha)
168+
if key in by_size_align:
169+
idx = set_to_idx[by_size_align[key]]
170+
print(f" {idx:>5}", end="")
171+
else:
172+
print(f" {'—':>3}", end="")
173+
# Show max servable sizeclass for highest alpha
174+
best_alpha = max(a for a in aligns if (n, a) in by_size_align)
175+
best_set = by_size_align[(n, best_alpha)]
176+
print(f" max_sc={max(best_set) if best_set else 0}")
177+
178+
# Show how many indexes are actually distinct per alignment tier
179+
print()
180+
for alpha in aligns:
181+
indices_at_tier = set()
182+
for n in range(1, ARENA + 1):
183+
key = (n, alpha)
184+
if key in by_size_align:
185+
indices_at_tier.add(set_to_idx[by_size_align[key]])
186+
print(f" Tier α={alpha:>3}: {len(indices_at_tier)} distinct indexes used")
187+
188+
# Show the progression: for each block_size n (at max alignment),
189+
# what's the index and what new size class was unlocked?
190+
print()
191+
print("=" * 80)
192+
print("PROGRESSION at max alignment (block at address 0):")
193+
print("As block size grows, which size classes become servable?")
194+
print("=" * 80)
195+
prev_set = frozenset()
196+
for n in range(1, 129):
197+
# At address 0, alignment is infinite, so alpha is huge
198+
key = (n, min(align(0), ARENA))
199+
# Actually address 0 won't appear with all sizes. Use highest alpha.
200+
# Let's compute directly
201+
servable = frozenset(
202+
sc for sc in size_classes if n >= sc # at perfect alignment, just need n >= sc
203+
)
204+
if servable != prev_set:
205+
new = sorted(servable - prev_set)
206+
print(f" n={n:>4}: +{new} (total {len(servable)} classes)")
207+
prev_set = servable
208+
209+
print()
210+
print("=" * 80)
211+
print("PROGRESSION at alignment 1 (worst case):")
212+
print("=" * 80)
213+
prev_set = frozenset()
214+
for n in range(1, 257):
215+
key = (n, 1)
216+
if key not in by_size_align:
217+
continue
218+
servable = by_size_align[key]
219+
if servable != prev_set:
220+
new = sorted(servable - prev_set)
221+
print(f" n={n:>4}: +{new} (total {len(servable)} classes)")
222+
prev_set = servable
223+
224+
print()
225+
print("=" * 80)
226+
print("PROGRESSION at alignment 4:")
227+
print("=" * 80)
228+
prev_set = frozenset()
229+
for n in range(1, 257):
230+
key = (n, 4)
231+
if key not in by_size_align:
232+
continue
233+
servable = by_size_align[key]
234+
if servable != prev_set:
235+
new = sorted(servable - prev_set)
236+
print(f" n={n:>4}: +{new} (total {len(servable)} classes)")
237+
prev_set = servable
238+
239+
if __name__ == "__main__":
240+
main()

0 commit comments

Comments
 (0)