直接用[]bool处理标志位会静默出错,因越界访问返回false而无法区分“未设置”与“索引非法”,且内存浪费大、gc效率低;应改用基于[]uint64的bitmap,索引和位偏移须用uint64和uint避免32位系统截断或符号扩展问题。

为什么直接用 []bool 处理标志位会静默出错
Go 的 []bool 不做越界检查,<code>b[10000000] 超长时返回 <code>false,你无法区分“未设置”还是“索引非法”。更糟的是内存浪费:1000 万布尔值占 10MB,而等效 <code>[]uint64 只需 1.25MB。GC 扫描大片 <code>[]bool 也比扫描 <code>[]uint64 慢 3–5 倍。
- 别写
var flags []bool,改用 <code>type BitMap struct { bits []uint64; len uint64 } - 初始化容量必须按 64 向上取整:
cap := (totalUsers + 63) / 64 - 每次读写前校验
wordIdx:若 <code>wordIdx >= uint64(len(b.bits)),直接返回或 panic
GetBit 和 <code>SetBit 怎么写才不崩在 32 位系统上
常见错误是用 int 当索引(32 位系统截断)、用 <code>int8(1) 做位移、或写 <code>(b.bits[i] >> bitIdx) & 1(负数右移补符号位)。
- 索引必须用
uint64:<code>wordIdx := userID / 64 - 位偏移必须用
uint:<code>bitIdx := uint(userID % 64) - 设置位:
b.bits[wordIdx] |= (1 1 是 <code>uint64 类型 - 读取位:
(b.bits[wordIdx] & (1 <li>并发写同一 <code>uint64 时,用 <code>atomic.OrUint64(&b.bits[wordIdx], 1
Base64 编码里位移和掩码为什么不能用除法替代
Base64 把每 3 字节(24 位)拆成 4 组 6 位索引,核心是“右移对齐 + 掩码截断”。用除法或字符串操作会引入分支、分配临时内存、破坏 CPU 流水线;位运算无分支、零分配、编译后直接对应硬件指令。
- 三字节合成:
val := uint(src[0]) <li>提取第 1 组(bit 18–23):<code>val >> 18 & 0x3F - 掩码
0x3F(二进制 <code>00111111)确保只留低 6 位,防止高位污染 - 填充处理时,低位补 0 后仍走同一逻辑,自然产出合法索引,再按规则补
=
什么时候该放弃手写位图,换 roaringbitmap
手写 []uint64 在 1000 万用户、100 标签内完全可控;但一旦标签基数超 1%(比如“城市”字段有 300 个取值),位图会严重稀疏——内存暴涨、AND 变慢、缓存局部性崩坏。
- 小规模(
- 中等规模(100 万–5000 万 + 稀疏多值字段):切
roaringbitmap,它自动分块压缩,<code>Cardinality() 是 O(1) - 大规模(> 5000 万):必须用
roaring.Bitmap,<code>ToArray() 才分配内存,避免中间切片 OOM
真正容易被忽略的不是“怎么写”,而是“什么时候不该写”——位运算的高效,永远绑定在数据密度和访问模式上。稀疏场景下强行手写,性能反而不如 map。
golang免费学习笔记(深入):立即使用
在学习笔记中,你将探索golang的核心概念和高级技巧!











