0%

昨天晚上合并完新的代码准备提交上去但是 CI 跑不了,有一个单元测试报错。然后照着官方的 git-bisect 文档找问题。这个工具比想象中要好用很多,大部分时间都花在了构建项目上面(每次修改 HEAD 之后都要重新构建才能测试)。半个小时后锁定了问题在一个具体的提交上。

接下来我需要将我的代码和这部分有问题的代码分离。我的分支是 main,远程的分支是 origin/develop,有错误的分支是 origin2/develop。因为 main 合并了一部分 origin2/develop 的代码才导致了错误。分支的结构是这样的:

* --(若干次提交)-- origin/develop  --(若干次提交)-- main
 \                                              / (若干次提交,非线性,想要舍弃掉)
   --- origin2/develop -------------------------

因为我要保证 origin/develop 这个我们的仓库总是 fast-forward 合并,所以我先以这个分支为基准创建新的本地分支。然后将在 main 中(有我的新代码)、不在 next 中(因为我本来就在 next 的位置上)、不在 origin2/develop 中(有错误)的提交重播。

git checkout -b next origin/develop
git cherry-pick main ^next ^origin2/develop

步骤

docker save image:tag | zstd -o image.tar.zst
  • docker save 是将镜像输出到标准输出流,docker save -o 是将输出写入到文件。
  • zstd -o 是将压缩数据写入文件,而 zstd -c 是将压缩的数据输出到标准输出流。

为什么用 zstd 而不是 gzip 来压缩

  1. zstd 在 ubuntu/debian 中有现成的包可以下载,安装非常轻松。
  2. 压缩速度非常快,压缩比可以接受。
  3. 我的使用场景是在服务器之间传输文件,带宽很高,所以不想花太多时间在压缩和解压上。

新的拔键器到货(😓不好用)

之前的文章说键盘附送的拔键器会伤到键帽,所以买了个新的拔键器:

拔轴要把整个拔轴器按下去,退轴的时候要拨弹簧卡扣,也不是不好用吧,但是我觉得比原来的拔轴器麻烦。拔键要向下压弹簧才能把拔键器卡进键位,实际感受是比较累的,既不如送的拔键器小巧,又不如它好用。而且拔键的一端卡不到某些键位里面去,以下两张图中都是因为碰到了键盘边框所以无法卡进去:

~~不过,在拔键器能够卡进去的位置,我还是优先使用这个拔键器,这样不会损坏键帽的边缘。(心疼!)~~已经完全放弃了,还是原来的小型拔轴器好用,出坑了就用手指甲刮一下。

更新

  1. 2024 年 6 月 26 日:解决在桌面上移动键盘时脚撑发出刺耳摩擦声的办法:在脚撑上面贴上透明胶布。
  2. 2024 年 6 月 29 日:现在键盘因为多次拔键拔轴已经是战损状态了,尤其是卫星轴和定位板之间卡不紧、有松动,问题相当大。很扎心: 以后选键盘还是选个一开始就能接受轴体的,不要再换轴了吧 (*^_^*) 。总觉得 V75K 的卫星轴专门顺着阿尼亚轴的短行程调过(或者是挑选过),换阿尼亚轴匹配度更高一点。我现在没有别的轴体了(卖了),就试了鲸海轴,空格左侧是有 空键程 的,换成阿尼亚轴就好一点。

买键盘的历史

本人小白,以前都是用薄膜键盘,这两个月买过以下键盘:

  1. 京东京造 K3,佳达隆矮茶轴,对于长时间用笔记本薄膜键盘的我来说非常容易上手。但是 84 按键(无论从手感还是视觉上都)非常紧凑,有时候找不到方向键,按 ctrl 和 shift 按键也经常按错。大写锁定灯在按键下方,不容易看到。另外对于 340 元这个价位来说,这样一把塑料机身的键盘显得有点廉价。因为是矮轴2024 年 6 月 1 日:我现在才知道并不是所有矮轴都是无边框)因为是 keychron 的无边框设计,难买适配的防尘罩。也担心放到书包里面被电脑压弯,不过主要退货原因还是经常按错按键。
  2. 宁芝有线 35g 87 键。刚到手在桌面上就放不平,在 NGA 上也搜到了相同问题,对宁芝的品控非常担心,秒退。
  3. Keychron K1 Pro,87 键佳达隆矮茶轴。因为上次 84 键太紧凑不太好找方向键,所以这次买了 87 键的。好处是现在能够更容易看到 CapsLock 指示灯。到手的时候备用键帽有质量问题换不上去,请客服补发了一次。不知道为什么,感觉按键敲下去就是没有 K3 好用。而且可能是因为矮轴机身薄 + 填充足,想要给料但是机身又塞不下,所以是鼓起的!已退。关于我觉得没有 K3 好用,可能和这个截图中的评论有关:
  4. IKBC 红轴有线,选了个好看的配色,还送了键帽。键盘左侧区域有钢丝音,尤其是 tab 按键,OEM 键帽用起来也挺顺手。拉出大脚撑,在桌面上移动键盘,就会发出粉笔垂直划黑板的声音 1 ,其他键盘的脚撑就不会有刺耳的声音。没有明显质量问题(钢丝音是普遍现象我觉得也还能忍),而且刚到手的那段时间我确实觉得还不错(不过买了 V75K 就不喜欢 IKBC 了),所以留了。
  5. ATK V75K 闪银。外观非常好看、便宜(相对于 1~3)、三模、料足,用的原厂键帽会比 IKBC 要矮一些。阿尼亚轴手感还是舒服的,但是我不喜欢麻将音 2,换了静音灰雪轴。空格左右的卫星轴好像有点高度不一致,从左右两次按下去键帽,可以感觉得到左侧能按的更低一些。然后不小心掰断了空格按键,用胶布贴起来了。后来送修了 3。自己虽然买了空格键帽,但是是在键盘送修之后才到货的。设计缺点也还是有,比如 80 按键(75% 配列)右侧没有 ctrl,而且也没有 home 和 end 按键,注释和批量选中代码有点不方便;CapsLock 按键灯在按键的正下方,侧着看很难看到,使用起来等同于没有灯。很喜欢所以留了。

Caution

简直是品控中招体质。

选购键盘的想法

  1. 小配列键盘好看便携,但是可能会不习惯。小配列键盘的大写锁定按键灯一般都很难看到(除非专门设计在侧边),而买常见尺寸的键盘没这个问题。我觉得小配列可能比较适合发烧友,因为键少换的快(我这个 80 键的全部换轴一次要 40 分钟),而且每次买键帽或者买轴都会买的更少、价格更低。小键盘放在桌面上也有一种工艺品的感觉,但是要和按键少这个缺点放在一起好好考虑一下。京造 K3 容易按错可能是因为距离相近、而且键帽都是一样的高度,不像 V75K 正常轴体、用原厂键帽,仅凭手感就能区分方向键和上面紧邻的回车。
  2. 矮轴键盘对于从薄膜键盘转过来的人非常容易上手,但是因为售价普遍偏高、难买防尘盖,不建议入。如果不能接受 IKBC 这样的高键帽,可以看看 XDA 或者原厂键帽这种比较低一点的,不一定非要去买矮轴。NuPhy 看起来倒还不错(不知道上手怎么样),做工精致、有边框、送防尘罩,但是价格超出我的预算了。
  3. 轴体选择。我到现在都没有思路……静音灰雪轴和阿尼亚轴我都不是特别喜欢,但是感觉买新的轴应该也差不多,不如省下钱不买了。买热插拔的键盘的确会有一种买轴的诱惑。

UNIX 中的 printf

Single UNIX Specification 中允许 printf 使用 %m$*m$ 的形式来绑定第 m 个参数,其中 m 从 1 开始计数。如果使用了这种表达方式,则所有参数都要指定序号,而且不能遗漏参数。这样做的好处是:可以多次使用同一个参数。

请注意,参数是 %m$*m$ 这两个结构索取的。这意味着以下两行的含义相同:

printf("%*d", width, num);
printf("%2$*1$d", width, num);

首先 %2$ 索要了参数 num 用来表示输出的数字,然后 *m$ 索要了参数 width 来表示输出的宽度。这和我们看到的 %*d 的参数顺序有差异:%*d 中就好像输出数字这个参数是 d 索取的一样。相关链接:StackOverflow 的提问

C99 中没有规定这种指定序号绑定参数的方式。

修改配置文件

网上一般推荐的做法是这种: https://linuxiac.com/how-to-change-docker-data-directory/

修改 /etc/docker/daemon.json,向其中加入:

{ "data-root": "/new/docker/root" }

或者在 systemd 的服务启动程序中指定数据的路径。

不修改配置文件,只转移数据

但是我们实验室的服务器没有采用以上方案,却通过其他方式完成了从标准位置 /var/lib/docker 向其他位置的数据转移,而且两个文件夹中的数据是一样的。可以想到的方法有三种:软链接、硬链接、挂载。一个一个分析。

事情起因

在第一次使用 nsys 的时候发现 cudaLaunchKernel 非常耗时。网友觉得 cudaLaunchKernel 超过了 50ms 就觉得很大了,但我在服务器上测是 400~500ms!

我想到的可能原因:

  • 服务器的 CPU 和 GPU 负载太高。用环境变量 CUDA_VISIBLE_DEVICES 换到同一台服务器上相对比较空闲的卡,发现 kernel 启动时间从平均 440 ms 减少到了 372 ms,因此可以排除 GPU 负载高的影响。
  • 启动 kernel 时申请的资源太多。我们自己写的 kernel 每个 block 都是固定 512 线程数,GPU 为 3090,每个 SM 上最大能容纳 1024 个线程;对 cudnn 的调用(比如卷积核)看了一下是用满了 1024 线程数。
  • GPU 在 warmup 阶段。

真正原因

GPU 在 warmup 阶段

安装

nsys 来自包 cuda-nsight-systems-11-7(和自己的 CUDA 版本对应一下)。

在网上一直都没有搜到安装方式,官网也说的不明不白。尝试过两个错误的包:

  • nsight-systems:这个是 Qt 写的图形界面程序,提供的二进制可执行程序是 nsight-sys
  • nvidia-nsight:这个是 Java 写的图形界面程序,提供的二进制可执行程序是 nsight

使用

nsys profile --stat=true xxx 运行完程序后得到 report1.nsys-rep,report 后面的编号是自动增长的,而 --stat=true 表示用文本形式在命令行输出信息。如果有条件可以把 report1.nsys-rep 下载下来然后用本地的图形化程序加载分析。

但是这样导出的程序看不到 CUDA 内存占用。为了看到 CUDA 内存占用情况,考虑:

起因:每日一题官解看不明白

今天(2024 年 5 月 4 日)做 Leetcode 每日一题又没有做出来,最后抄了答案。题目是这样的:1235. 规划兼职工作

思路是先按照 endTime 排序,然后再 dp,然后 dp 中用二分查找求满足“自己的 endTime 小于等于当前元素 startTime ” 的元素数量。但是官方解答的 std::upper_bound 传参实在是看迷糊了。

二分查找:AoS 和 SoA 的比较

一般数组(Array of Structures 或 AoS)排序好之后,直接用 std::lower_bound / std::upper_bound 做就好了。但如果数组里面存的不是可以直接用 operator< 或者自定义 comp 比较的值,写 comp 就比较伤脑筋。比如这道题里面为了减少数据拷贝(或者内存使用量),只对下标排序:

注:comp 指的是自定义的比较器。

int jobScheduling(vector<int> &startTime, vector<int> &endTime, vector<int> &profit) {
    vector<int> idx(n);
    iota(idx.begin(), idx.end(), 0);
    sort(idx.begin(), idx.end(), [&](int i, int j) -> bool { return endTime[i] < endTime[j]; });
    // ...
}

C++11 之后 enum 的新增功能

enum class 是 C++11 提供的功能,为了更好理解后文的内容,我们先看看 C++11 之后 enum 有什么变化。参考资料见 https://en.cppreference.com/w/cpp/language/enum

有作用域枚举

有作用域枚举(Scoped enumerations)使用 enum class|struct ClassName 声明,以区别于原来的无作用域枚举。无作用域枚举的枚举量可以直接在外围名字空间中访问,当枚举类有名字且 C++ 版本至少为 C++11 时,可以通过 枚举名::枚举量 访问;有作用域枚举只能通过 枚举名::枚举量 访问。

有作用域枚举的默认底层类型是 int

例子:

void example() {
    enum class Color { red, green = 20, blue };
    Color r = Color::blue;
}