|
| 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