Hint
这里的日常开发就不包括如何写各类公开课的实验了,最简单的方法就是使用 WSL,在 WSL 里随便玩,反正环境坏了也可以重装
日常开发
2025-10-20 更新
已经换到了
MacBook Air M4做开发,所以少了 Linux 到 Windows 的同步,但是服务器到本地的同步还是存在的,因此下面的内容也不算过时 在 MacOS 与 Linux 就可以使用rsync -av来做增量同步了
由于我在两台设备上进行开发,虽然都是 wsl 环境(写 C/C++ 的暂时不考虑 Windows),所以多端的同步比较重要。
首先,确保 WSL 的版本与发行版一致,我选择的都是 WSL2 和 Ubuntu-24.04
我使用 github 来进行同步,步骤如下:
- 新建一个私有仓库
- 把 remote 的地址链接到两个电脑上
- 写完一个小版本后开始同步
注意,最好的方式是写完一个版本开始同步,但我们经常会存在功能一天写不完,晚上下班回去之后可能还会写一写的需求,我的做法是:
- 新建一个分支
- 提交暂存,然后同步
- 在这个分支上写完一整个功能后,向主分支提 pr,然后把 commit 历史 squash 再进行合并
这样就能保证主分支确实是一个功能一次 commit,保证 commit 清爽
Tip
关于如何写 commit,可以参考文章 Git Commit message 编写指南
实验
由于组内服务器的使用不是每个人一个账号,并且也没有使用容器,总体来说比较随意(这样可能也会出现一些装环境的时候把服务器跑崩的问题,但鉴于组内没有做 system 的,其实这个风险也没有太大)
我的做法一般是,从 github 上 clone 我的代码 ,运行自己编写的 Dockerfile 和 docker-compose.yml,这样方便管理,因为一个文件夹就是一个容器(或者好几个容器,取决于实验的设计)
怎么跑实验
一次可能需要跑好多 benchmark ,一个 benchmark 可能需要跑好几遍取平均,所以基本上是多进程跑,可以写一个 bash 脚本来做到这一点,例如:
./main -f data/instance/xxxx1.in -o eval/instance/1/xxxx1.out
./main -f data/instance/xxxx2.in -o eval/instance/1/xxxx2.out
./main -f data/instance/xxxx3.in -o eval/instance/1/xxxx3.out
./main -f data/instance/xxxx4.in -o eval/instance/1/xxxx4.out
./main -f data/instance/xxxx5.in -o eval/instance/1/xxxx5.out
./main -f data/instance/xxxx6.in -o eval/instance/1/xxxx6.out可以通过命令:cat run.sh | xargs -P60 -d'\n' -n1 bash -c 来并行执行
xargs -P60 -d'\n' -n1 bash -c
-
xargs是一个命令,用于从标准输入读取数据,并将其作为参数传递给另一个命令 -
-P60选项:xargs会并行地运行最多 60 个进程。这意味着xargs会同时启动 60 个bash -c进程来处理输入 -
-d'\n'选项:指定输入的分隔符为换行符\n。这意味着xargs会将输入的每一行作为一个单独的参数 -
-n1选项:指定每次传递给bash -c的参数数量为 1。这意味着xargs会将每一行作为一个单独的命令传递给bash -c -
bash -c:bash -c是一个命令,用于在新的 shell 中执行指定的命令。-c选项后面跟着的是要执行的命令字符串但是,如何产生这个
run.sh又成为一大问题
生成实验脚本
这里我介绍一种很简单的方法:Python
我们可以让 LLM 写一个简单的 Python 脚本,遍历实例文件夹,来生成 run.sh ,例如一个 generate_run_cmd.py 简单如下:
import glob
import os
l = glob.glob("data/**/*.graph", recursive=True)
cmd = "./bin/Release/net8.0/fss {} > {}"
output_dir = "solution/"
os.makedirs(output_dir, exist_ok=True)
cmds = []
for f in l:
output_dir = "solution/"
if f.endswith(".graph"):
output_dir += f.split("/")[-2]
os.makedirs(output_dir, exist_ok=True)
output_file = output_dir + '/' + os.path.basename(f).replace(".graph", ".sol")
cmds.append(cmd.format(f, output_file))
with open("scripts/run.sh", "w+") as f:
for c in cmds:
f.write(c + "\n")
然后,我们可以再写一个 start 的脚本如下:
#!/bin/bash
python3 scripts/generate_run_cmd.py
cat run.sh | xargs -P60 -d'\n' -n1 bash -c这样就可以一行命令 bash start 跑完一次实验了
(懒人版)生成实验脚本
更进一步
如果我们有多个求解器,每个求解器参数不同,输入文件不同,我又想生成的时候统一生成运行脚本,应该怎么做?
怎么统计数据
这部分当然没有其他的路子,最简单的方法就是 LLM + Python,例如一个简单的 stats.py 如下所示:
import glob
import re
solution_lists = glob.glob('solution/*/**.sol', recursive=True)
pattern = r'\[solution\]\s+(\d+)\s+\[time\]\s+(\d+\.\d+)\s+us'
result = []
for file in solution_lists:
with open(file, 'r') as f:
content = f.readline().strip()
match = re.search(pattern, content)
if match:
solution_size = match.group(1)
time = match.group(2)
result.append(
{
"instance": file.split('/')[-1].split('.')[0],
"solution_size": solution_size,
"time(us)": time
}
)
else:
result.append(
{
"instance": file.split('/')[-1].split('.')[0],
"solution_size": "N/A",
"time(us)": "TLE"
}
)
import pandas
df = pandas.DataFrame(result)
df.to_csv('solution_results.csv', index=False)
当然,你必须要做的事情就是:格式化输出
例如上面的例子中,我的输出被格式化为
[solution] 1035 [time] 8031755250.422 us总之,Python 很好用,尤其在这种小事情上,Python + LLM 可以省下很多统计数据的时间,甚至还可以 直接画图
Info
如果你想使用中间数据,那也可以使用
jupyter,这也是十分推荐的方式,因为脚本很小,数据量也不算很大,比深度学习友好很多,甚至你还可以写一点实验结果的分析在笔记上,防止自己的灵感忘记了
怎么知道实验结束(懒人版)
参考文章 实验结束自动发送邮件 来进行设置