实用代码小技巧

即查即用

Environment

设置环境变量

在 ~/.bashrc

1
export CUDACXX=/usr/local/cuda/bin/nvcc

命令行

1
source ~/.bashrc

externally-managed-environment

error: externally-managed-environment

  1. 直接捂嘴
1
sudo mv /usr/lib/python3.12/EXTERNALLY-MANAGED /usr/lib/python3.12/EXTERNALLY-MANAGED.bak
  1. 创建虚拟环境
1
2
python -m venv ENV_DIR
source ENV_DIR/bin/activate

ENV_DIR 指定存放环境的目录

退出环境:

1
deactivate

无法解析主机名

1
2
3
wdl@bm-2209pbv:~$ sudo ls /root
sudo: unable to resolve host bm-2209pbv: Name or service not known
app

其实是无伤大雅的,就是有点烦人。解决方式:

1
sudo vim /etc/hosts

显示内容:

1
127.0.0.1   localhost

修改为:

1
127.0.0.1   localhost bm-2209pbv

挂载硬盘

1
2
3
4
sudo fdisk -l	# 查看设备名
sudo mkdir /mnt/newdisk
sudo mount /dev/sdb1 /mnt/newdisk
df -h 	# 检查挂载情况

设置开机自动挂载:编辑 /etc/fstab 文件,添加一行:

1
/dev/sdb1    /mnt/newdisk    ext4    defaults    0

可参考:Linux中将多块新硬盘合并成一个,挂载到/mysqldata目录下_linux两块硬盘合并成一块-CSDN博客

用户管理

添加用户

1
sudo adduser wdl

给予 sudo 权限

1
sudo usermod -aG sudo wdl

验证 sudo 权限

1
2
su - wdl
sudo ls /root

删除用户

1
2
sudo deluser wdl
sudo deluser --remove-home wdl	# 删除主目录与文件

Conda

image-20250702155235670

注意:conda create 的时候指定 python 版本,可以避免出现 error: externally-managed-environment

清华源

1
pip3 install numpy -i https://pypi.tuna.tsinghua.edu.cn/simple
1
2
3
conda config --add channels https://mirrors.tuna.tsinghua.edu.cn/anaconda/pkgs/free/
conda config --add channels https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/conda-forge
conda config --add channels https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/msys2/

查看系统架构

1
uname -m

查看发行版信息

1
cat /etc/os-release

查看CPU

1
lscpu

关键信息:

  • Architecture: 架构,如 x86_64。
  • CPU(s): 总逻辑核心数。
  • Socket(s): CPU 插槽数(物理 CPU 数量)。
  • Core(s) per socket: 每个物理 CPU 的核心数。
  • Model name: CPU 型号,例如 Intel(R) Xeon(R) Gold 6248R。
  • CPU max MHz: 最大睿频 (Turbo Boost) 频率。
  • Flags: CPU 支持的指令集

SSH

ssh permission

Permissions 0664 for ‘/home/wdl/.ssh/id_wdl’ are too open.

1
chmod 600 /home/wdl/.ssh/id_wdl

ssh passphrase

linux

1
2
3
eval $(ssh-agent -s)
ssh-add ~/.ssh/id_rsa
ssh-add -l	# 验证是否成功

windows:打开PowerShell

1
2
Start-Service ssh-agent
ssh-add C:\Users\wdl\.ssh\id_rsa

Host key has changed

ssh -v 报错:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
@    WARNING: REMOTE HOST IDENTIFICATION HAS CHANGED!     @
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
IT IS POSSIBLE THAT SOMEONE IS DOING SOMETHING NASTY!
Someone could be eavesdropping on you right now (man-in-the-middle attack)!
It is also possible that a host key has just been changed.
The fingerprint for the ED25519 key sent by the remote host is ...
Please contact your system administrator.
Add correct host key in C:\\Users\\.../.ssh/known_hosts to get rid of this message.
Offending ECDSA key in C:\\Users\\.../.ssh/known_hosts:58
Host key for 192.168.1.79 has changed and you have requested strict checking.
Host key verification failed.

原因:远程主机的主机密钥(Host Key)发生了变化,而本地的 known_hosts 文件中记录的旧密钥与当前服务器的密钥不匹配,导致了 SSH 客户端拒绝连接

解决方案:更新本地的 known_hosts 文件,找到并删除与 192.168.1.79 相关的行,重新连接

Docker

Docker permission

1
docker: permission denied while trying to connect to the Docker daemon socket...

解决方式:

1
2
sudo usermod -aG docker wdl
newgrp docker // or reboot

验证:

1
groups

如果临时跑命令,也可以直接 sudo docker run

命令大全

Docker 命令大全 | 菜鸟教程

进入docker:

1
docker exec -it megatron-lm /bin/bash

Q:为什么存在旧容器,但是 docker ps 不显示?

A:docker ps 只默认列出“正在运行”的容器,应该使用 docker ps -a

Docker 无法识别 GPU

1
docker: Error response from daemon: could not select device driver "" with capabilities: [[gpu]]

原因:Docker 没有安装或启用 NVIDIA Container Toolkit,导致它无法识别并使用宿主机的 GPU 资源

安装 NVIDIA Container Toolkit:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
# 添加官方仓库
curl -fsSL https://nvidia.github.io/libnvidia-container/gpgkey | sudo gpg --dearmor -o /usr/share/keyrings/nvidia-container-toolkit-keyring.gpg

curl -s -L https://nvidia.github.io/libnvidia-container/stable/deb/nvidia-container-toolkit.list | \
  sed 's#deb https://#deb [signed-by=/usr/share/keyrings/nvidia-container-toolkit-keyring.gpg] https://#g' | \
  sudo tee /etc/apt/sources.list.d/nvidia-container-toolkit.list

# 安装
sudo apt-get update
sudo apt-get install -y nvidia-container-toolkit

配置 Docker 使用 NVIDIA runtime:

1
sudo nvidia-ctk runtime configure --runtime=docker

重启 Docker:

1
sudo systemctl restart docker

Files

scp

1
scp -r xxx:path yyy:path

scp: Connection refused

1
2
3
scp -P 58107 -r ./data  wdl@10.18.18.107:/home/wdl/
ssh: connect to host 10.18.18.107 port 58107: Connection refused
scp: Connection closed

Connection refused 说明 10.18.18.107 的 58107 端口没有 sshd 在监听,或被防火墙拦截

1
2
3
sudo ss -tulnp | grep sshd

tcp   LISTEN 0      4096                    *:22               *:*    users:(("sshd",pid=4790,fd=3),("systemd",pid=1,fd=116))

说明 sshd 仍在默认 22 端口,改为 -P 22 即可

rsync

1
rsync -avzP src dst

image-20250619182739859

-z:在传输过程中对数据进行压缩

-P:进度条与断点续传

文件夹下的所有文件

统计文件夹下的所有文件大小

1
du -sh .

查看所有文件

1
ls -la

校验是否损坏

1
md5sum test.txt

压缩与解压

1
2
tar -czvf archive.tar file1 file2 directory
tar -zxvf archive.tar

权限

查看权限

1
2
ls -l x.sh
ls -ld /x/y/

修改

1
2
sudo chown wdl:wdl x.sh
sudo chown -R wdl:wdl /x/y

Markdown

强制换页

1
<div STYLE="page-break-after: always;"></div>

空格

img

图片并排显示

1
2
3
4
<center class="half">
    <img src="图片链接1" width="200"/>
    <img src="图片链接2" width="200"/>
</center>

Git

git diff

仅输出不同文件名

1
git diff --name-only .. ..

git submodule

1
git submodule update --init --recursive

临时保存工作进度

场景:需要临时保存当前的工作进度,切换到另一个分支,之后再回来继续工作,但是又不希望 commit

1
2
3
git stash push -u -m "..."
git stash pop	# 应用最近一次的储藏,并从储藏列表中删除它
git stash apply # 不会删除

如果你多次使用 git stash,它会把你的修改都存成一个列表:

1
2
git stash list
git stash pop stash@{1}

强行回退

git log 找到希望回退到的 commit 的哈希值

1
2
3
4
5
6
7
8
git reset --hard <commit-hash>

# --force 会强行覆盖远程分支
git push --force

# 更安全的 --force-with-lease
# 它会先检查远程分支是否和你上次拉取时一样,如果被别人更新过,则推送失败
git push --force-with-lease

git revert

1
2
3
4
5
6
7
git revert <commit-hash>

# 只将撤销的更改应用到工作目录和暂存区,但不自动创建新的提交。
# 可以一次性撤销多个不连续的提交,然后把它们合并成一个单独的 "revert" 提交
git revert -n <commit-hash-1>
git revert -n <commit-hash-2>
git commit -m "Revert features X and Y due to issues"

修改分支名

1
2
3
4
5
git checkout old_branch
git branch -m new_branch
git push origin --delete old_branch
git push origin new_branch
git push --set-upstream origin new_branch

无法连接到 github.com

首先尝试 curl -v https://github.com,输出“详细信息: GET with 0-byte payload”

查看是否能解析域名:nslookup github.com

解析失败,图形界面手动修改网络适配器 DNS:

  1. 打开“控制面板” → “网络和 Internet” → “网络和共享中心” → “查看网络状态和任务” → “更改适配器设置”;
  2. 右键正在用的网络连接 → “属性”;
  3. 双击 “ Internet 协议版本 4 (TCP/IPv4)”;
  4. 选择“使用下面的 DNS 服务器地址”:
    • 首选 DNS:8.8.8.8
    • 备用 DNS:1.1.1.1
  5. 点击“确定”保存

还是不行,开启 V2RayN 代理,查看参数设置:

image-20250819205441617

以及“ v2rayN 设置” →“ Core 类型” 改为 Xray_core

最后设置 git proxy:

1
2
git config --global http.proxy  socks5h://127.0.0.1:7890
git config --global https.proxy socks5h://127.0.0.1:7890

LLM

瓶颈计算

  • 计算受限时间 (T_compute) = 总计算量 / 峰值计算能力 (FLOPS)
  • 内存受限时间 (T_memory) = 总内存访问量 / 内存带宽 (Bytes/s)

瓶颈判断规则:如果 T_memory > T_compute,那么该操作就是 内存受限 的。

对 CPU 来说:

理论 GFLOPS = (CPU 核心数) * (CPU 频率 GHz) * (每个周期能执行的指令数)

测内存带宽:

1
2
3
4
5
6
7
8
9
# 下载源码
wget https://www.cs.virginia.edu/stream/FTP/Code/stream.c

# -fopenmp: 开启 OpenMP 支持,利用所有 CPU 核心去访问内存,这才能测出最大带宽
# -DSTREAM_ARRAY_SIZE: 设置一个足够大的数组,必须远大于你所有 CPU Cache 的总和,以确保测试的是内存而非缓存。例如设置为 8GB (2^33 bytes)
gcc -O3 -fopenmp -DSTREAM_ARRAY_SIZE=8000000000 stream.c -o stream_test

export OMP_NUM_THREADS=$(nproc)
./stream_test

输出结果:

1
2
3
4
5
6
7
-------------------------------------------------------------
Function    Best Rate MB/s  Avg time     Min time     Max time
Copy:           125331.4     0.102223     0.101890     0.102802
Scale:          125430.2     0.102196     0.101810     0.102555
Add:            139682.4     0.114755     0.114545     0.114947
Triad:          140348.1     0.114197     0.113999     0.114493
-------------------------------------------------------------
  • Copy: a(i) = b(i),测试一次读和一次写的带宽。
  • Scale: a(i) = q * b(i),一次读,一次写。
  • Add: a(i) = b(i) + c(i),两次读,一次写。
  • Triad: a(i) = b(i) + q * c(i),两次读,一次写。这是最常被引用的指标,最能代表真实应用中的内存访问模式

llama.cpp 打印算子

 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
ggml_barrier(params->threadpool);

if (ith == 0 && strncmp(dst->name, "kq-", 3) == 0) {
    const struct ggml_tensor *t = src1;
    FILE *fp = NULL;
    char file_name[100];

    sprintf(file_name, "data/attention_score_%s.log", dst->name);
    fp = fopen(file_name, "a+");

    fprintf(fp, "dst->name: %s\n", dst->name);
    fprintf(fp, "num_kv: %lld, num_tokens: %lld, num_head: %lld\n", t->ne[0], t->ne[1], t->ne[2]);

    for (int i2 = 0; i2 < t->ne[2]; ++i2) {
        fprintf(fp, "i2: %d\n", i2);
        for (int i1 = 0; i1 < t->ne[1]; ++i1) {
            fprintf(fp, "i1: %d\n", i1);
            for (int i0 = 0; i0 < t->ne[0]; ++i0) {
                fprintf(fp, "i0: %d: %f\n",
                    i0, *((float *)((char *)t->data + i2 * t->nb[2] + i1 * t->nb[1] + i0 * t->nb[0])));
            }
            fprintf(fp, "\n");
        }
        fprintf(fp, "\n\n");
    }

    fclose(fp);
}

Megatron-LM 测试

单机多卡:Dense 模型

1
2
3
# under /home/developer/wdl
git clone https://github.com/NVIDIA/Megatron-LM.git
GIT_LFS_SKIP_SMUDGE=1 git clone https://hf-mirror.com/openai-community/gpt2

Docker创建:

1
docker run -it --name megatron-lm   --gpus=all   --ipc=host   -v /home/wdl:/workspace   -w /workspace   nvcr.io/nvidia/pytorch:24.01-py3   /bin/bash

数据预处理:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
mkdir data
cd data
wget https://hf-mirror.com/bigscience/misc-test-data/resolve/main/stas/oscar-1GB.jsonl.xz
xz -d oscar-1GB.jsonl.xz
cd ..

python megatron-lm/tools/preprocess_data.py \
  --input ./data/oscar-1GB.jsonl \
  --output-prefix meg-gpt2 \
  --vocab-file ./gpt2/vocab.json \
  --tokenizer-type GPT2BPETokenizer \
  --merge-file ./gpt2/merges.txt \
  --append-eod \
  --workers 8
  
mv meg-gpt2_text_document.bin data/
mv meg-gpt2_text_document.idx data/

截至目前的目录结构:

1
2
3
4
5
6
7
root@a6a69636d44e:/workspace# ls -la
total 20
drwxrwxr-x  5 1001 1001 4096 Apr 27 04:22 .
drwxr-xr-x  1 root root 4096 Apr 27 04:07 ..
drwxr-xr-x  2 root root 4096 Apr 27 04:22 data
drwxrwxr-x  4 1001 1001 4096 Apr 27 02:53 gpt2
drwxrwxr-x 14 1001 1001 4096 Apr 27 02:25 megatron-lm

正式训练:

1
2
3
cd ./megatron-lm/examples/gpt3
cp train_gpt3_175b_distributed.sh train_gpt3_test.sh
vim /workspace/megatron-lm/examples/gpt3/train_gpt3_test.sh

Dense 模型参数量约 6.6B,显存占用约 90GB,配置文件如下:

 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
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
#!/bin/bash

export CUDA_DEVICE_MAX_CONNECTIONS=1

GPUS_PER_NODE=8
# Change for multinode config
MASTER_ADDR=localhost
MASTER_PORT=6000
NUM_NODES=1
NODE_RANK=0
WORLD_SIZE=$(($GPUS_PER_NODE*$NUM_NODES))

CHECKPOINT_PATH="/workspace/checkpoint"
TENSORBOARD_LOGS_PATH="/workspace/logs"
VOCAB_FILE="/workspace/gpt2/vocab.json"
MERGE_FILE="/workspace/gpt2/merges.txt"
DATA_PATH="/workspace/data/meg-gpt2_text_document"

DISTRIBUTED_ARGS=(
    --nproc_per_node $GPUS_PER_NODE
    --nnodes $NUM_NODES
    --master_addr $MASTER_ADDR
    --master_port $MASTER_PORT
)

GPT_MODEL_ARGS=(
    --num-layers 32
    --hidden-size 4096
    --num-attention-heads 32
    --seq-length 1024
    --max-position-embeddings 2048
    --attention-backend auto # Can use (flash/fused/unfused/local)
)

TRAINING_ARGS=(
    --micro-batch-size 1
    --global-batch-size 1536
    # --rampup-batch-size 16 16 5859375
    --train-iters 10
    --weight-decay 0.1
    --adam-beta1 0.9
    --adam-beta2 0.95
    --init-method-std 0.006
    --clip-grad 1.0
    --fp16
    --lr 6.0e-5
    --lr-decay-style cosine
    --min-lr 6.0e-6
    --lr-warmup-fraction .001
    --lr-decay-iters 430000
)

MODEL_PARALLEL_ARGS=(
	--tensor-model-parallel-size 1
	--pipeline-model-parallel-size 1
)

DATA_ARGS=(
    --data-path $DATA_PATH
    --vocab-file $VOCAB_FILE
    --merge-file $MERGE_FILE
    --split 949,50,1
)

EVAL_AND_LOGGING_ARGS=(
    --log-interval 10
    --save-interval 10000
    --eval-interval 1000
    --save $CHECKPOINT_PATH
    --load $CHECKPOINT_PATH
    --eval-iters 10
    --tensorboard-dir $TENSORBOARD_LOGS_PATH
)

torchrun ${DISTRIBUTED_ARGS[@]} pretrain_gpt.py \
    ${GPT_MODEL_ARGS[@]} \
    ${TRAINING_ARGS[@]} \
    ${MODEL_PARALLEL_ARGS[@]} \
    ${DATA_ARGS[@]} \
    ${EVAL_AND_LOGGING_ARGS[@]}

单机多卡:MoE 模型

MoE 模型参数量 20BA2B,显存占用约 81GB,配置文件如下:

  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
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
#!/bin/bash

export CUDA_DEVICE_MAX_CONNECTIONS=1

GPUS_PER_NODE=8
# Change for multinode config
MASTER_ADDR=localhost
MASTER_PORT=6000
NUM_NODES=1
NODE_RANK=0
WORLD_SIZE=$(($GPUS_PER_NODE*$NUM_NODES))

CHECKPOINT_PATH="/workspace/checkpoint"
TENSORBOARD_LOGS_PATH="/workspace/logs"
VOCAB_FILE="/workspace/gpt2/vocab.json"
MERGE_FILE="/workspace/gpt2/merges.txt"
DATA_PATH="/workspace/data/meg-gpt2_text_document"

DISTRIBUTED_ARGS=(
    --nproc_per_node $GPUS_PER_NODE
    --nnodes $NUM_NODES
    --master_addr $MASTER_ADDR
    --master_port $MASTER_PORT
)

GPT_MODEL_ARGS=(
    --no-masked-softmax-fusion
    --disable-bias-linear
    --untie-embeddings-and-output-weights
    --position-embedding-type rope
    --no-rope-fusion
    --normalization RMSNorm
    --swiglu
    --num-layers 32
    --hidden-size 2048
    --ffn-hidden-size 6144
    --num-attention-heads 32
    --group-query-attention
    --num-query-groups 4
    --kv-channels 128
    # --qk-layernorm
    --num-experts 128
    --moe-ffn-hidden-size 768
    --moe-router-topk 8
    --moe-router-dtype fp32
    --moe-aux-loss-coeff 1e-3
    --moe-token-dispatcher-type alltoall
    --moe-router-load-balancing-type aux_loss
    --use-mcore-models
    --rotary-percent 1.0
    --rotary-base 1000000
    --no-bias-swiglu-fusion
    --seq-length 1024
    --max-position-embeddings 2048
    --attention-backend auto # Can use (flash/fused/unfused/local)
)

TRAINING_ARGS=(
    --micro-batch-size 1
    --global-batch-size 1536
    # --rampup-batch-size 16 16 5859375
    --train-iters 10
    --weight-decay 0.1
    --adam-beta1 0.9
    --adam-beta2 0.95
    --init-method-std 0.006
    --clip-grad 1.0
    --bf16
    --lr 6.0e-5
    --lr-decay-style cosine
    --min-lr 6.0e-6
    --lr-warmup-fraction .001
    --lr-decay-iters 430000
)

MODEL_PARALLEL_ARGS=(
	--tensor-model-parallel-size 1
	--pipeline-model-parallel-size 1
    --expert-model-parallel-size 8
)

DATA_ARGS=(
    --data-path $DATA_PATH
    --vocab-file $VOCAB_FILE
    --merge-file $MERGE_FILE
    --split 949,50,1
)

EVAL_AND_LOGGING_ARGS=(
    --log-interval 10
    --save-interval 10000
    --eval-interval 1000
    --save $CHECKPOINT_PATH
    --load $CHECKPOINT_PATH
    --eval-iters 10
    --tensorboard-dir $TENSORBOARD_LOGS_PATH
)

torchrun ${DISTRIBUTED_ARGS[@]} pretrain_gpt.py \
    ${GPT_MODEL_ARGS[@]} \
    ${TRAINING_ARGS[@]} \
    ${MODEL_PARALLEL_ARGS[@]} \
    ${DATA_ARGS[@]} \
    ${EVAL_AND_LOGGING_ARGS[@]}

多机训练

1
docker run -it --name megatron-lm   --gpus=all   --ipc=host --network=host --privileged=true -v /home/wdl:/workspace   -w /workspace   nvcr.io/nvidia/pytorch:24.01-py3   /bin/bash

解析:

  1. –network=host 让容器直接用宿主机网卡,IP、端口全部可见
  2. –privileged=true,如果没有这条的话,docker 内用不了 IB。ibv_devices 可查看可用 IB,如果不可用,在接下来的脚本中,如果设置 NCCL_DEBUG=INFO,日志中会出现 NCCL INFO NET/IB: No device found. –privileged=true 相当于把 /dev 也挂载了进去,就能使用 IB

配置文件(以node_rank=0为例):

  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
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
#!/bin/bash
set -ex

# Runs the "175B" parameter model

export CUDA_DEVICE_MAX_CONNECTIONS=1
# export NCCL_DEBUG=INFO
export TORCH_DISTRIBUTED_BACKEND=nccl
export NCCL_SOCKET_IFNAME=bond0
export GLOO_SOCKET_IFNAME=bond0
export SKIP_P2P_PING=false
export DISTRIBUTED_JOB=true
export NCCL_IB_HCA=mlx5_0,mlx5_1,mlx5_2,mlx5_4

GPUS_PER_NODE=8
MASTER_ADDR=${MASTER_ADDR:-10.18.18.106}
MASTER_PORT=${MASTER_PORT:-6000}
NUM_NODES=${NUM_NODES:-2}
NODE_RANK=${NODE_RANK:-0}
WORLD_SIZE=$(($GPUS_PER_NODE * $NUM_NODES))

CHECKPOINT_PATH="/workspace/checkpoint"
TENSORBOARD_LOGS_PATH="/workspace/logs"
VOCAB_FILE="/workspace/gpt2/vocab.json"
MERGE_FILE="/workspace/gpt2/merges.txt"
DATA_PATH="/workspace/data/meg-gpt2_text_document"

DISTRIBUTED_ARGS=(
    --nproc_per_node $GPUS_PER_NODE
    --nnodes $NUM_NODES
    --master_addr $MASTER_ADDR
    --master_port $MASTER_PORT
    --node_rank $NODE_RANK
)

GPT_MODEL_ARGS=(
    --no-masked-softmax-fusion
    --disable-bias-linear
    --untie-embeddings-and-output-weights
    --position-embedding-type rope
    --no-rope-fusion
    --normalization RMSNorm
    --swiglu
    --num-layers 32
    --hidden-size 2048
    --ffn-hidden-size 6144
    --num-attention-heads 32
    --group-query-attention
    --num-query-groups 4
    --kv-channels 128
    # --qk-layernorm
    --num-experts 128
    --moe-ffn-hidden-size 768
    --moe-router-topk 8
    --moe-router-dtype fp32
    --moe-aux-loss-coeff 1e-3
    --moe-token-dispatcher-type alltoall
    --moe-router-load-balancing-type aux_loss
    # --use-mcore-models
    --rotary-percent 1.0
    --rotary-base 1000000
    --no-bias-swiglu-fusion
    --seq-length 1024
    --max-position-embeddings 2048
    --attention-backend auto # Can use (flash/fused/unfused/local)
)

TRAINING_ARGS=(
    --micro-batch-size 1
    --global-batch-size 1536
    # --rampup-batch-size 16 16 5859375
    --train-iters 30
    --weight-decay 0.1
    --adam-beta1 0.9
    --adam-beta2 0.95
    --init-method-std 0.006
    --clip-grad 1.0
    --bf16
    --lr 6.0e-5
    --lr-decay-style cosine
    --min-lr 6.0e-6
    --lr-warmup-fraction .001
    --lr-decay-iters 430000
)

MODEL_PARALLEL_ARGS=(
	--tensor-model-parallel-size 1
	--pipeline-model-parallel-size 1
    --expert-model-parallel-size 8
)

DATA_ARGS=(
    --data-path $DATA_PATH
    --vocab-file $VOCAB_FILE
    --merge-file $MERGE_FILE
    --split 949,50,1
)

EVAL_AND_LOGGING_ARGS=(
    --log-interval 10
    --save-interval 10000
    --eval-interval 1000
    --save $CHECKPOINT_PATH
    --load $CHECKPOINT_PATH
    --eval-iters 1
    --tensorboard-dir $TENSORBOARD_LOGS_PATH
)

torchrun ${DISTRIBUTED_ARGS[@]} pretrain_gpt.py \
    ${GPT_MODEL_ARGS[@]} \
    ${TRAINING_ARGS[@]} \
    ${MODEL_PARALLEL_ARGS[@]} \
    ${DATA_ARGS[@]} \
    ${EVAL_AND_LOGGING_ARGS[@]}

解析:

  1. 注意比单机脚本多一行 –node_rank $NODE_RANK

  2. MASTER_ADDR 为主节点实际 IP,PORT 随意

  3. export NCCL_SOCKET_IFNAME=bond0,这里根据实际网卡名修改,也可能是 eth0,用 ifconfig 找到 inet 和主 IP 一致的那个网卡名就是

  4. export GLOO_SOCKET_IFNAME=bond0,否则报错:RuntimeError: Gloo connectFullMesh failed …

  5. export NCCL_IB_HCA=mlx5_0,mlx5_1,mlx5_2,mlx5_4,指定使用的 IB,不使用 mlx5_3 原因见“集群网络”章节

  6. Megatron-LM 启动时会在 –data-path 目录下自动生成一套 index + cache 文件,文件名中包含数据集哈希值。如果两节点的存储不共享,其他节点上会找不到 cache 文件。解决方法是主节点处理完文件之后,手动传到其他节点上即可

集群网络

查看网卡拓扑:

1
nvidia-smi topo -m

结果:

image-20250828133905899

image-20250828133927135

image-20250828134703232

跨机通信就是:卡0->NIC->NIC->卡15

NIC0-4 就是 Infiniband,ibstat 命令可以看信息

image-20250828134148787

Rate: 400 就代表 400gbps,可以发现 mlx5_3 rate 只有 200,是存储IB,需要跳过

Debug

VScode python

launch.json:

 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
{
    "version": "0.2.0",
    "configurations": [
        {
            "name": "sync test",
            "type": "debugpy",
            "request": "launch",
            "python": "/disk2/wdl/miniconda3/envs/infinigen/bin/python",
            "program": "flex_llama3.py",
            "args": [
                "--model", "/disk2/wdl/llama-3.2-3b-instruct",
                "--path", "/disk2/wdl/FlexGen/llama_weights",
                "--offload-dir", "/disk2/wdl/FlexGen/offload_dir",
                "--prompt-len", "7",
                "--gen-len", "10",
                "--gpu-batch-size", "1",
                "--num-gpu-batches", "2",
                "--prefill-batch-size", "512",
                "--percent", "100", "0", "0", "0", "100", "0",
                "--attn-sparsity", "0.1",
                "--compress-weight",
            ],
            "console": "integratedTerminal",
            "cwd": "/disk2/wdl/FlexGen/flexgen",
        },
    ]
}

VScode C++

 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
{
    "version": "0.2.0",
    "configurations": [
        {
            "name": "qkv fuse",
            "type": "cppdbg",
            "request": "launch",
            "program": "/home/wdl/powerinfer-refactor/build_qkv/bin/llama-cli",
            "args": [
                "-t", "4",
                "-no-cnv",
                "--temp", "0.6",
                "--top-k", "20",
                "--top-p", "0.95",
                "--no-warmup",
                "-n", "256",
                "--samplers", "'temperature;top_k;top_p'",
                "-m", "/home/wdl/our-20b-q4_0.gguf",
                "-p", "Once upon a time ",
                // "-f", "/home/wdl/context_1000.txt",
            ],
            "stopAtEntry": true,
            "cwd": "/home/wdl/powerinfer-refactor/build_qkv/bin/",
            "environment": [],
            "externalConsole": false,
            "MIMode": "gdb",
            "setupCommands": [
                {
                    "description": "Enable pretty-printing for gdb",
                    "text": "-enable-pretty-printing",
                    "ignoreFailures": true
                },
                {
                    "description": "Set Disassembly Flavor to Intel",
                    "text": "-gdb-set disassembly-flavor intel",
                    "ignoreFailures": true
                }
            ]
        }
    ]
}

Segmentation fault (core dump)

程序发生 Segmentation fault (core dump) 之后:

1
sudo coredumpctl

如果没有出现信息,则需要:

1
2
3
sudo apt-get install systemd-coredump
sudo systemctl restart systemd-sysctl.service
echo "|/usr/lib/systemd/systemd-coredump %P %u %g %s %t %c %h" | sudo tee /proc/sys/kernel/core_pattern

这时候重新运行出错程序,如果还是不行,可能是因为为了防止程序错误地产生巨大的核心转储文件占满硬盘,默认情况下将core dump的大小限制为0,需要:

1
2
ulimit -c 				# 查看core文件大小限制
ulimit -c unlimited

sudo coredumpctl有输出之后:

1
coredumpctl gdb

默认会查看最近的一个core dump。gdb内用bt可以查看调用堆栈,用fr N可以去往第N层堆栈

VSCode

函数跳转

【经验分享】vscode c++ 函数无法跳转问题解决教程_vscode函数跳转插件-CSDN博客

解决vscode下C/C++indelisense插件函数跳转卡顿不流畅的问题_vscode代码跳转不稳定-CSDN博客

IntelliSense 卡顿问题

先删除 .cache/vscode-cpptools/ipch

还卡就没别的办法了,只能设置里 disable

image-20250620181402961

C/C++ Coding

常用数据结构

Map

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
std::map<int, std::string> m;

// 遍历
for (const auto& [k, v] : m) {
	std::cout << k << ": " << v << "\n"; 
}

// 查找
auto it = m.find(1);
if (it != m.end()) {
	return it->second;
}

// 自定义对 array<int, 26> 类型的哈希函数
auto arrayHash = [fn = hash<int>{}] (const array<int, 26>& arr) -> size_t {
    return accumulate(arr.begin(), arr.end(), 0u, [&](size_t acc, int num) {
    	return (acc << 1) ^ fn(num);
    });
};

unordered_map<array<int, 26>, vector<string>, decltype(arrayHash)> mp(0, arrayHash);
特性unordered_mapmap
实现方式哈希表(hash table)红黑树(red-black tree)
排序无序(不保证顺序)有序(按键升序排序)
查找效率平均 O(1),最坏 O(n)稳定 O(log n)
插入效率平均 O(1)O(log n)
删除效率平均 O(1)O(log n)
  • 访问(带边界检查):m.at(key)
  • 插入:insert({k,v}), emplace(k, v)
  • 删除:erase(it), erase(key)
  • 区间插入:insert(it_first, it_last)
  • 区间删除:erase(it_first, it_last)
  • 是否存在:count(key)

Vector

  • 访问(带边界检查):m.at(pos)
  • 插入:push_back(x), emplace_back(args…), insert(it_pos, val)
  • 区间插入:insert(it_pos, it_first, it_last), insert(it_pos, n, val)
  • 删除:pop_back()
  • 区间删除:erase(it_first, it_last)
  • 改变大小:resize(n), resize(n, val)
  • 预分配空间(不改变大小):reserve(n)
  • 排序:sort(v.begin(), v.end(), [](int a, int b) { return a > b; }); // 降序
  • 数组尾:back()

Stack/Queue

  • 插入:push(x), emplace_back(args…)
  • 移除:pop(x)
  • 读顶部:top()
  • 队列头/尾:front(), back()

priority_queue 类似,可以用 push(x), emplace(args…),默认为大顶堆

小顶堆的实现:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
// 1
priority_queue<int, vector<int>, greater<int>> small_heap;

// 2
struct Status {
	int val;
	bool operator < (const Status &rhs) const {
		return val > rhs.val;
	}
};

priority_queue <Status> q;

deque 双端队列,可以用 pop_back(), pop_front()

String

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
std::string s1(5, 'x');         // "xxxxx"
std::string s2("abc"); 
std::string s3(s2, 1, 2);       // 子串 "bc"

int len = s.size(); 			// 长度,不包括 '\0'

s1.insert(5, " dear");          // 在下标 5 处插入
s1.erase(5, 5);                 // 从下标 5 起删 5 字符

size_t pos = s2.find("bc");   		// 找不到返回 string::npos
std::string sub = s1.substr(6, 5); 	// 从下标 6 起 5 字符

sort(s2.begin(), s2.end());			// 排序
std::reverse(s1.begin(), s1.end());

string → char:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
std::string s = "hello";
const char* p = s.c_str();   // 返回 '\0' 结尾的 const char *

char* p = s.data();          // C++17 起非常量重载
p[0] = 'H';

std::vector<char> v(s.begin(), s.end());

char buf[32];
strcpy(buf, s.c_str());      // 拷贝到本地数组
strncpy(buf, s.c_str(), sizeof(buf)-1);

char → string:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
const char* psz = "hello";
std::string s(psz);          // 拷贝直至 '\0'

std::vector<char> v{'a','b','c'};
std::string s(v.begin(), v.end());

char tmp[256];
scanf("%255s", tmp);        // 假设 C 风格读取
std::string user(tmp);      // 再变成 C++ 字符串

std::string s = "hello";
for(char& c : s) c = toupper(c);   // HELLO

与 std::stringstream 联动:

1
2
3
4
std::string line = "123 45.6";
std::stringstream ss(line);
int i; double d;
ss >> i >> d;

常用库函数:

函数一句话说明
strcpy(char *dest, const char *src)src(含 \0)全部拷到 dest
strncpy(char *dest, const char *src, size_t n)最多拷 n 字节,不保证 \0 结尾
strcat(char *dest, const char *src)src 追加到 dest 末尾
strncat(char *dest, const char *src, size_t n)最多追加 n 字节,始终在结尾加 \0
函数返回值
strcmp(const char *s1, const char *s2)<0,0,>0
strncmp(const char *s1, const char *s2, size_t n)同上
strcasecmp / stricmp (POSIX/Windows)大小写不敏感比较
函数说明
strlen(const char *s)不计 \0 的字符数,复杂度 O(n)
strchr(const char *s, int ch)第一次出现某字符,返回指针;找不到返回 nullptr
strrchr(const char *s, int ch)右往左找字符
strpbrk(const char *s, const char *accept)第一次出现 accept 中任意字符的位置
strstr(const char *hay, const char *needle)找子串,返回指针;找不到返回 nullptr

ASCII表

1
2
3
4
5
6
'0' -> 48
'9' -> 57
'A' -> 65
'Z' -> 90
'a' -> 97
'z' -> 122

C代码使用C++代码

例:现在需要调用在某个 .c 文件中,调用一个由 .hpp 与 .cpp 文件定义的函数

步骤:

  1. 新建一个 .h 文件,将 .hpp 文件改造为 .h 文件
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
#ifdef __cplusplus
extern "C" {
#endif

void TraceEventStart(const char *name);
void TraceEventEnd();

#ifdef __cplusplus
} // extern "C"
#endif
  1. 在 .cpp 文件中 include 这个 .h 文件,同样地,也要用 extern “C” 包裹起来
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
#ifdef __cplusplus
extern "C" {
#endif

void TraceEventStart(const char *name) {
    TRACE_EVENT_BEGIN(event_category, perfetto::StaticString{name});
}

void TraceEventEnd() {
    TRACE_EVENT_END(event_category);
}

#ifdef __cplusplus
} // extern "C"
#endif

​ 3. 在 .c 文件里 include .h文件

文件读写

C++

流方法:fstream 对象在销毁时会自动调用 close()

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
#include <fstream>
#include <string>

// read
std::ofstream log(get_log_filename(layer_id, head_id), std::ios::binary | std::ios::app);
int value = 100;
log << value << " ";
log.write(reinterpret_cast<const char*>(&value), sizeof(value)); // binary
log.close();

// write
std::ifstream log(filename, std::ios::binary);
log >> value;
log.read(reinterpret_cast<char*>(&value), sizeof(value));

// 使用 std::fstream 必须手动指定读写模式
std::fstream iofile("data.bin", std::ios::in | std::ios::out | std::ios::binary);

常见的打开模式:

标识符含义说明
std::ios::in读模式为读取而打开文件。ifstream 的默认模式。
std::ios::out写模式为写入而打开文件。ofstream 的默认模式。
std::ios::binary二进制模式以二进制方式处理文件,而非文本模式。读写速度快且无损
std::ios::app追加模式(append) 写入操作将在文件末尾进行。
std::ios::trunc截断模式(Truncate) 如果文件已存在,打开时会清空其所有内容。ofstream 默认行为。
std::ios::ate打开后定位到末尾(At End) 文件打开后,立即将位置指针移动到文件末尾。可以写入或移动到任何位置。

stringstream 操纵字符串:

1
2
3
4
5
6
7
8
#include <sstream>
#include <string>

std::string get_log_filename(size_t layer_id, size_t head_id) {
    std::stringstream ss;
    ss << "prefill_cache" << "/cache_L" << layer_id << "_H" << head_id << ".log";
    return ss.str();
}

C

fread 方法:

“w” 写, “r” 读, “a” 追加, “b” 二进制

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
#include <cstdio>

FILE *f = fopen(input->name, "w");
if (f) {
    const char* text = "hello";
    fwrite(text, sizeof(char), 5, f); // 写入数据
    fflush(f);                        // 强制将缓冲区内容写入文件
    fclose(f);                        // 必须手动关闭
}

FILE *f = fopen("data.bin", "rb");
if (f == NULL) {
    perror("Error opening file");
    return -1;
}

int value;
size_t items_read = fread(&value, sizeof(int), 1, f);

if (items_read == 1) {
	printf("Read value: %d\n", value);
}

fclose(f);

fget 方法:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
FILE *f = fopen("log.txt", "r"); // "r" = read (文本读)
if (f) {
    char line[256]; // 定义一个行缓冲区

    while (fgets(line, sizeof(line), f) != NULL) {
        printf("%s", line);
    }

    fclose(f);
}

fprintf 方法:读取格式化文本

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
FILE *f = fopen("config.txt", "r");
if (f == NULL) {
    perror("Error opening file");
    return -1;
}

int layer_id, head_id;
fscanf(f, "%d %d", &layer_id, &head_id);
printf("Layer: %d, Head: %d\n", layer_id, head_id);

FILE *f = fopen("config.txt", "w");

fprintf(f, "Log Entry:\n");
fprintf(f, "Processed Layer %d, Head %d.\n", layer_id, head_id);

fclose(f);

同时进行读和写,应该使用以下三种带 + 的模式之一:

模式含义文件不存在时文件已存在时
“r+”读写更新打开失败 (返回 NULL)不清空内容,指针在文件开头
“w+”写读更新创建新文件清空内容 (截断为0),指针在文件开头
“a+”追加读写创建新文件不清空内容,初始读指针在开头,写指针在末尾

fopen 只接受 char* 文件名,方法有如下几种:

1
2
3
4
5
6
7
# std::string
std::string filename = "logits_dump_" + std::to_string(gen_len) + ".txt";
FILE *f = fopen(filename.c_str(), "w");

# only C
char filename[256];
snprintf(filename, sizeof(filename), "logits_dump_%lld.txt", gen_len);

Python Coding

文件读写

with 语句块结束时,无论是否发生异常,Python 都会自动关闭文件

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
with open("log.txt", "w", encoding="utf-8") as f:
    f.write("\n")

with open("log.txt", "r", encoding="utf-8") as f:
    # 方式一:一次性读取所有内容
    content = f.read()
    print(content)

    # 方式二:逐行读取
    for line in f:
        print(line.strip()) # strip() 去除行尾换行符

# 写二进制文件
data = b'\xDE\xAD\xBE\xEF'
with open("data.bin", "wb") as f:
    f.write(data)

# 读二进制文件
with open("data.bin", "rb") as f:
    read_data = f.read()
    print(read_data)

读取变量

如果一个变量不存在,自动读取另一个变量

1
n_experts = self.hparams.get("num_experts", self.hparams.get("moe_num_experts"))

其他

检查打通网络

为了确定另一个节可以连通到 10.18.18.106:5678

1
2
# 在 10.18.18.106 上执行
nc -lv 5678        # 或者:nc -l 5678

看到提示 Listening on 0.0.0.0 5678 就说明服务已就绪

1
2
3
4
# 在另一台节点执行
telnet 10.18.18.106 5678
# 如果没有 telnet,也可以用 nc
nc -vz 10.18.18.106 5678

显示成功即可

Licensed under CC BY-NC-SA 4.0
本文总访问量(loading...)
使用 Hugo 构建
主题 StackJimmy 设计
本站访客数(loading...)人次