在2024年的IOCCC(国际混淆C代码大赛)中,有一个非常炫技的作品:用 27 行 C 代码实现了完整的 Llama 大语言模型推理引擎。本文将深入剖析这个极致压缩的代码实现。
1. 从编译命令入手
1.1 运行脚本分析
首先看try.sh脚本的关键部分:
1 2 3 4 5 6 7 8 9 10 11
| case "$SELECT" in 0) NAME="prog" ;; 1) NAME="linux" ;; 2) NAME="eeyore" ;; 3) NAME="rainman" ;; 4) NAME="snoop" ;; esac
make CC="$CC" "${NAME}_openmp"
|
1.2 编译时宏定义机制
关键发现:虽然所有personality使用同一份源代码,但通过编译时宏定义生成不同的可执行文件。推测的Makefile结构:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| prog: prog.c $(CC) -DM=\"model\" \ -DS=\"You are a helpful AI assistant\" \ prog.c -o prog -lm
linux: prog.c $(CC) -DM=\"model\" \ -DS=\"You are a Linux compiler. Respond with compiler errors\" \ prog.c -o linux -lm
eeyore: prog.c $(CC) -DM=\"model\" \ -DS=\"You are Eeyore, pessimistic and gloomy\" \ prog.c -o eeyore -lm
|
原始代码中使用这些宏的地方:
1 2 3 4 5 6
| f = open(M, f);
sprintf(o = X, "[INST] %s%s [/INST]", s ? "" : "<<SYS>>\n" S "\n<</SYS>>\n\n", Y);
|
2. 宏展开和变量分析
2.1 核心宏定义
原始代码使用了大量宏来压缩代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| ```
### 2.2 类型定义展开
```c
a(int8_) C; a(in) I; a(floa) F; a(struc){C*c;F*f;} t;
typedef int8_t C; typedef int I; typedef float F; typedef struct { C* c; F* f; } t;
|
2.3 关键常量解析
1 2 3 4 5 6 7 8
| enum { Z = 32, W = 64, E = 2*W, D = Z*E, H = 86*E, V = '}\0' };
|
2.4 Magic String解析
这个字符串实际上编码了模型的配置参数。每个Unicode字符的数值代表不同的配置:
| Unicode字符 |
编码值 |
可能的含义 |
| 炾 |
U+70FE (28926) |
词汇表大小或特殊token ID |
| ો |
U+0AEB (2795) |
Padding token ID |
| İ |
U+0130 (304) |
开始token ID |
| 䃃 |
U+40C3 (16579) |
序列长度限制 |
| 璱 |
U+74B1 (29873) |
结束token ID |
| ᝓ |
U+1753 (5971) |
层数信息 |
| ၎ |
U+104E (4174) |
注意力头数 |
3. 代码结构和功能分析
3.1 内存布局和初始化
原始代码的内存映射部分:
1 2 3
| A = mmap(0, 8e9, 1, 2, f = open(M, f), 0); x 2)~f?i[G]=malloc(3e9):exit(puts(M" not found"));
|
对应的功能:
- 使用mmap映射8GB模型文件到内存
- 分配2×3GB工作空间用于计算
3.2 核心函数j()解析
这是神经网络计算的核心函数,让我们对照原始代码逐行分析:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25
| I j(I e, F*o, I p, F*v, t*X) { w = 1e-5; for(i=0; i<(e==V?0:D); i++) w+=r[i]*r[i]/D; for(i=0; i<c; i++) o[i]=r[i]/sqrt(w)*gamma[i]; #pragma omp parallel for(h=0; h<p; h++) }
|
对照原始代码的Transformer计算流程:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53
| R = s++; N Z){
for(i=0; i<3; i++) j(k, L, D, i?G[i-1]:v, e[i]+k); for(k=0; k<2; k++) { for(i=0; i<D; i++) { float theta = pos / exp((i%128)/14.0); float sin_theta = sin(theta); float cos_theta = cos(theta); complex_rotate(Q_or_K[i], sin_theta, cos_theta); } } #pragma omp parallel for(h=0; h<32; h++) { for(i=0; i<seq_len; i++) { for(k=0; k<128; k++) scores[i] += Q[k] * K[i][k] / sqrt(dim); scores[i] = exp(scores[i]); sum += scores[i]; } for(i=0; i<seq_len; i++) scores[i] /= sum; } for(i=0; i<2; i++) j(k+32, L, 11008, i?K:a, d[i]+k); for(i=0; i<11008; i++) a[i] *= K[i]/(exp(-a[i])+1); j(V, a, D, L, d[2]+k); }
|
3.4 主推理循环
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35
| for(;;) { printf("\n\n"); scanf("%[^\n]%*c", Y); sprintf(X, "[INST] %s%s [/INST]", s ? "" : "<<SYS>>\n" S "\n<</SYS>>\n\n", Y); for(f=0; !f; z-=!f) { for(f=V; --f && (f[P][z] || memcmp(f[P], o, z)); ) ; } w = j(W, r, V, k, n); for(i=0; i<V; i++) if(k[i]>w) { w = k[i]; $ = i; } }
|
3.5 复杂宏g的解析
1 2 3 4 5 6 7 8 9 10 11 12
|
for(i=0; i<(s%11%5); i++) for(k=0; k<(s/6&33); k++) u[i][k] = (t){(C*)A, A+s*D/4}, A+=1088*s;
g(&m, V) g(&n, V) g(e, D) g(d, H)
|
4. 技术亮点总结
- mmap直接映射:避免加载模型到内存的开销
- int8量化:使用8位整数存储权重,配合float32缩放因子
- 原地计算:最小化内存分配,重用缓冲区
- Magic String:将配置参数编码在Unicode字符中
- 零配置文件:所有配置都内嵌在代码或编译参数中
5. 性能优化
5.1 并行计算
5.2 向量化友好
- 连续内存访问模式
- 批量计算设计
- 缓存友好的数据布局(这一点很值得学习)
6. 总结
虽然代码可读性极差,但其技术含量令人惊叹:
优点:
这个项目完美诠释了IOCCC的精神:通过极致的技术展示C语言的威力,同时也提醒我们——能做到不代表应该这样做。在实际项目中,代码的可读性、可维护性远比极致压缩更重要。