一个零依赖、单文件的协议中转站:让 Claude Code(Anthropic Messages 协议)和 Codex(OpenAI Responses 协议)都能用上 OpenCode Go 订阅里的 DeepSeek V4 Flash——把超低价的模型,接到最贵的客户端上。
macOS NT QQ 聊天记录解密
背景
最近出于兴趣研究一下 macOS 上新版 NT QQ 的聊天记录存储机制。发现其使用了加密的 SQLite 数据库来存储消息数据。
环境信息
- 系统: macOS
- 软件: NT QQ (新版腾讯 QQ)
- 数据库类型: SQLCipher (加密 SQLite)
- 加密算法: AES-256 + HMAC-SHA1
数据定位
NT QQ 的用户数据主要存储位置:
1 | ~/Library/Application\ Support/QQ/ |
在这个目录下可以找到两个关键的数据库文件:
nt_msg.clean.db- 主消息数据库(约 2.8GB)profile_info.db- 用户配置信息(约 9.8MB)
密钥获取
NT QQ 将解密密钥存储在一个文本文件中,通常命名为类似 @qqkey 的文件。通过查看该文件可以找到数据库的解密密码。
注意: 不同版本的 QQ 可能使用不同的密钥存储方式和位置。
解密过程
1. 导出 SQL Dump
使用 sqlite3 命令行工具配合正确的加密参数导出数据:
1 | sqlcipher nt_msg.clean.db <<EOF |
2. 修复 SQL Dump
由于数据库可能处于事务状态,导出的 SQL 文件会以 ROLLBACK 结尾,需要替换为 COMMIT:
1 | cat raw_dump.sql | sed -e 's|^ROLLBACK;\( -- due to errors\)*$|COMMIT;|g' | sqlite3 nt_msg_fixed.db |
3. 读取解密后的数据
1 | sqlite3 nt_msg_fixed.db ".tables" |
可以看到多个数据表,主要包括:
1 | c2c_msg_table # 私聊消息表 |
注意事项
- PRAGMA kdf_iter: NT QQ 使用的是
4096次迭代,而非 SQLCipher 的默认值4000。这点很重要,如果参数不对会导致解密失败。 - 事务处理: 导出的 SQL dump 可能包含未提交事务,需要手动修复。
- 数据完整性: 建议先将解密后的数据库备份再进行查询操作。
结语
通过以上步骤,就可以成功解密 NT QQ 的聊天记录并导出数据。整个过程中最重要的是获取正确的加密密钥和配置参数。这个研究过程让我对 Qt 应用的数据存储机制有了更深的理解。
免责声明: 本文仅用于技术学习和个人数据备份,请勿用于非法用途或侵犯他人隐私。
What Happens When You Increase num_warps in Triton — 寄存器压力的实证调查
动机
在编写 Triton kernel 时,num_warps 是最常调节的编译参数之一。直觉上,更多的 warp 意味着更高的 occupancy,应该提升性能。但在某些场景下,增加 num_warps 反而会导致显著的性能回退——非单调的寄存器压力崩溃。
本文通过一系列实验,系统调查了以下问题:
num_warps=16到底比num_warps=2慢多少?- 变慢的物理机制是什么?是传统的 local memory spill,还是别的什么?
- 这个现象是 cherry-pick 的巧合,还是普遍存在的?
- Ampere 和 Blackwell 两代架构有何差异?
- 有没有办法故意触发真正的 STL/LDL spill?
所有代码和实验均在以下环境完成:
- GPU: NVIDIA GeForce RTX 3080 (SM 8.6, Ampere) + RTX 5060 Ti (SM 12.0, Blackwell)
- PyTorch: 2.11.0 + CUDA 13.0
- Triton: 3.6.0
- ptxas: Triton 捆绑版 (CUDA 2025)
代码全文见文末。
从零造一个 GPU 编译器:T0-GPU 作为课程项目的设计与实践
当 AI Agent 碰上 LLVM 后端:一场关于 GPU 指令调度的诚实实验
我们用 7 种 AI 调度 Agent 操控了 LLVM-AMDGPU 后端的 64 个指令调度决策点,产生了数百行不同的汇编代码——然后发现性能几乎没有变化。这是一个关于”编译器到底有多强”的诚实故事。
cuasmrl 部署实录:用 RL 优化 CUDA Kernel 指令调度
cuasmrl 部署实录:用 RL 优化 CUDA Kernel 指令调度
背景
cuasmrl 是一个基于 Triton 和 CuAssembler 的研究项目,核心思路是:
用强化学习(PPO)对 Triton 编译生成的 SASS 指令进行指令级重排序,通过调整 memory instruction 的相对位置来隐藏 latency,提升 kernel 的实际吞吐。
整个 pipeline 大致是:
1 | Triton JIT 编译 → cubin → CuAssembler 反汇编为 SASS |
最近在一台双卡服务器上成功复现了这套流程,记录一下踩过的坑。
实验室单卡GPU虚拟化:K3s + Z2JH + HAMi 实现多用户GPU共享
背景
实验室有一台 GPU 服务器(RTX 3080 + RTX 5060 Ti,共两张卡),之前多个人要用 GPU 只能排队——一个人独占整张卡,其他人等着。这种粗放模式显然效率太低,尤其是跑 Jupyter Notebook 做实验的时候,大部分时间 GPU 根本跑不满。
目标很明确:
- 一台物理机,两张消费级 GPU
- 多个用户同时使用 JupyterHub 做 PyTorch 开发
- 每个用户分到 4GB 显存 + 25% GPU 算力
- 一张卡最多切 5 个 vGPU,总共支撑 10 个用户并发
- 能可视化监控 GPU 使用情况
最终效果:一个 Helm values 文件 + 一套自动化安装脚本,在 K3s 上把 Z2JH、HAMi、HAMi-WebUI 全栈跑起来。
架构总览
1 | ┌──────────────────────────────────────────────────────────────┐ |
核心技术选型
K3s 替代标准 K8s
标准 K8s 太重了——kube-apiserver、etcd、controller-manager、scheduler 一堆组件,单机跑起来吃掉好几 GB 内存。我们是单节点场景,用 K3s 完美替代:
- 二进制只有 ~70MB,内存占用不到 500MB
- 自带 containerd、Flannel CNI
- 完全兼容标准 K8s API,Helm 直接可用
- 禁用了 traefik ingress,用 NodePort 直出端口更简单
HAMi 做 GPU 虚拟化
HAMi(异构算力管理中间件,原 vGPU-device-plugin)是开源的 GPU 共享调度方案,核心能力:
| 功能 | 说明 |
|---|---|
| 显存切分 | 一张物理 GPU 按 MB 级别切分,deviceSplitCount: 10 最多切 10 份 |
| 算力共享 | deviceCoreSharing: 1,按比例分配 GPU SM 算力 |
| 调度器 | 自定义 scheduler + admission webhook,binpack 策略优先塞满同一张卡 |
| 多厂商 | 除了 NVIDIA,还支持华为 Ascend、海光 DCU、寒武纪 MLU 等 |
用户 Pod 只需声明三个资源:
1 | resources: |
HAMi webhook 会自动注入 NVIDIA 可见设备环境变量,scheduler 把 Pod 调度到有足够剩余资源的 GPU 切片上。
Z2JH 做用户界面
JupyterHub 是学术界最流行的多用户 Notebook 方案,Z2JH 是它的 Helm Chart。关键配置:
1 | singleuser: |
认证插件使用 Dummy Authenticator,适合实验室内网环境。
魔改 HAMi-WebUI
原生的 HAMi-WebUI 有个 bug:GPU 使用率永远显示为 0。
Bug 根因
HAMi scheduler 在 Pod annotation 里编码 GPU 分配信息时,是按所有容器(包括 init containers)的顺序写入的。但 WebUI 解码时只遍历了 pod.Spec.Containers,忽略了 init containers,导致索引错位——应用容器拿到的永远是 init container(block-cloud-metadata)的空设备记录。
1 | Scheduler 写入 annotation: |
修复方案
修了三个文件:
1. util.go — 解码时考虑 init containers
1 | // 修复前:只算 Containers |
2. pod.go — 分配 device 索引时跳过 init containers
1 | // 修复前:直接用 i 作为索引,错位 |
3. node.go — 添加 nil 防护
当 POST 请求不带 filters 时 req.Filters 为 nil,会触发 panic。加上判空:
1 | if filters != nil { |
修复后 WebUI 正确显示各 GPU 使用率、显存和算力占用。
部署流程
整套部署压缩到三个脚本里:
1 | # 1. 安装 K3s + Helm + 部署 Z2JH |
HAMi 和 HAMi-WebUI 通过 Helm 单独部署:
1 | helm install hami HAMi/charts/hami -n kube-system |
访问方式
| 服务 | 地址 | 说明 |
|---|---|---|
| JupyterHub | http://<服务器IP>:31080 |
按配置认证登录 |
| HAMi-WebUI | http://<服务器IP>:32361 |
GPU 资源监控面板 |
资源分配示意
1 | RTX 3080 (10GB) RTX 5060 Ti (16GB) |
每个 JupyterHub 用户 Pod 自动分配到一个 vGPU 切片,跑 PyTorch 和独占一张卡体验完全一致——torch.cuda.is_available() 返回 true,nvidia-smi 能看到 GPU,只不过显存上限是 4GB。
总结
这套方案的核心价值:
- 消费级 GPU 也能虚拟化——不需要 Tesla/V100 这类数据中心卡
- 资源利用率大幅提升——从一人占整卡到十人共享
- 开箱即用——K3s 单机一条脚本拉起全栈
- 可观测——WebUI 实时看 GPU 占用,Prometheus 留存历史数据
- 易维护——Helm 统一管理,卸载一条命令清理干净
适合实验室、小团队、教学机房等预算有限但需要多用户 GPU 环境的场景。
AMD uProf 性能分析:矩阵乘法优化之旅
背景
CS210 课程的第二次作业要求使用性能分析工具对矩阵乘法程序进行 profiling。由于使用的是 AMD CPU,我选择了 AMD uProf 的 data_access 模式来分析 L1/DTLB 性能变化。
环境
- CPU: AMD (Family 0x17, Model 0x71, 24 核)
- OS: Linux Ubuntu 25.04, Kernel 6.14.0-37-generic
- 编译器: GCC 14.2.0,
-g -O3 -march=native - 分析工具: AMD uProf 5.3.518 (
data_accessconfig) - 矩阵大小: 2048×2048
测试用例
使用 Intel oneAPI 的 matrix_multiply_c 示例,包含 5 个优化版本:
| 版本 | 函数 | 优化策略 |
|---|---|---|
| 0 | multiply0 | 基础串行 (i-j-k 循环顺序) |
| 1 | multiply1 | 多线程 (pthreads, 同 i-j-k 顺序) |
| 2 | multiply2 | 循环交换 (i-k-j 顺序) |
| 3 | multiply3 | 循环交换 + 向量化提示 (#pragma ivdep) |
| 4 | multiply4 | Cache blocking (64 元素块) + 循环展开 |
编译与分析
每个版本独立编译并使用 AMDuProfCLI 进行数据访问分析:
1 | # 编译 |
结果
性能对比
| 版本 | 执行时间 (s) | MFLOPS | 加速比 |
|---|---|---|---|
| multiply0 | 127.78 | 134 | 1× |
| multiply1 | 6.89 | 2,495 | 18.5× |
| multiply2 | 0.77 | 22,320 | 166× |
| multiply3 | 0.73 | 23,605 | 175× |
| multiply4 | 0.15 | 112,524 | 836× |
L1 数据缓存分析
| 版本 | CPI | L1 DC Miss Ratio | L1 DTLB Miss Rate |
|---|---|---|---|
| multiply0 | 6.26 | 16.9% | 0.106 |
| multiply1 | 5.32 | 26.5% | 0.112 |
| multiply2 | 3.13 | 26.5% | 0.001 |
| multiply3 | 3.08 | 24.6% | 0.001 |
| multiply4 | 0.82 | 23.3% | 0.019 |
关键发现
1. 循环交换是最关键的优化
原始代码中,内层循环访问 b[k][j],其中 k 变化时会跨越整行内存(步长为 2048 个 double = 16KB)。这导致:
- 大量 L1 缓存未命中
- 高 DTLB 未命中率(跨页访问)
循环交换为 i-k-j 顺序后,b[k][j] 变为连续内存访问,DTLB 未命中率从 0.106 降至 0.001。
2. Cache Blocking 在大矩阵下效果显著
当矩阵大小为 2048×2048 时(每个矩阵 32MB,共 96MB),工作集远超 L1 缓存但可放入 L2。Cache blocking 将计算限制在 64×64 的块内,使工作集完全驻留在 L2 中:
- CPI 从 6.26 降至 0.82
- DRAM refill 从 6.21% 降至 0.01%
3. 多线程不能解决缓存问题
multiply1 虽然通过 16 线程获得了 18.5× 加速,但 L1 DC miss ratio 反而从 16.9% 恶化到 26.5%,因为多线程间的缓存竞争加剧了问题。
AMD Zen3 架构启示
在 AMD Zen3 架构上,矩阵乘法的性能瓶颈主要是:
- L1 数据缓存未命中 - 由非连续内存访问模式引起
- DTLB 未命中 - 跨页访问导致 TLB 压力
- CPI 过高 - 内存停顿导致流水线空闲
最有效的优化策略是 Cache blocking + 循环交换,将工作集限制在 L2 缓存内,同时保证块内访问的连续性。
上海科技大学校园网 OpenWrt 配置指南
DeepEP H20 GPU Replay Benchmark 教程
本教程详细说明如何在 NVIDIA H20 GPU 上运行 DeepEP replay benchmark。