0%

说明

在我写这个文章摘要代码片段的时候,Hugo 还没有支持按照元素截取,因此 summary 是纯文本、没有格式。

现在 Hugo 已经实现了带格式的截取。目前内置实现和我实现的区别是:内置实现是按照词数截取的,直到词数满足要求就停止添加新的元素,我的实现是按照顶级元素数量截取的(以前用 Hexo 插件有这个功能,因此我实现的也是这个)。

由于 Hugo 没有开放给 html 模板足够的功能,很多数据结构有点大材小用,Hugo 内置实现远远快于用 html 模板的实现。在我电脑上测试是快了好几倍,也就是说这个文章摘要功能拖累了渲染速度。

实现

themes/hugo-theme-next/layouts/partials/post/body.html 的基础上修改。原本内容类似于:

{{- with .ctx -}}
{{- if or (not $.IsHome) .Params.Expand -}}
  {{ .Content }}
{{- else -}}
  {{ .Summary }}
{{ end }}

准确的说是 conda 环境的提示重复了两次,实际上是同一个环境 env1,但显示出来是 (env1) (env1)

解决方案:

  1. 关闭 python.terminal.activateEnvironment,然后重载工作区,防止自动加载。
  2. 或者关闭 conda 的自动激活:conda config --set auto_activate_base false

我觉得 2 更方便一点,因为 vscode 调试 python 程序还是需要自动激活的。

解决 docker-compose 启动的容器无法使用 GPU

最近有人升级了服务器的 docker,我们创建容器有点问题。症状是这样:在命令行用 --gpus all 参数启动的都能正常使用 GPU(可以通过运行 nvidia-smi 命令测试),但是在 docker-compose.yml 文件中指定要使用 GPU 就不行。

之前的 docker-compose.yml 文件是这样:

version: "3.9"
services:
  my-dev:
    container_name: ${CONTAINER_NAME}
    image: ${IMAGE}
    ## -D 表示非 daemon 模式,-e 表示将日志输出到 stderr
    ## 如果有错误信息,可以用 docker compose logs 看到
    command: /usr/sbin/sshd -D -e
    restart: always
    volumes:
      - ./workspace:/workspace
      - ./data:/data
    ports:
      - "${PORT}:22"
    working_dir: /workspace
    shm_size: '8gb'
    pid: "host"
    security_opt:
      - seccomp:unconfined
    cap_add:
      - SYS_PTRACE
    deploy:
      resources:
        reservations:
          devices:
            - capabilities: [gpu]

出错原因是 devices 一栏写的不全面,要加上 count 属性才行。可能以前的 count 默认值是 all但是现在实测不写就不行

起因

同学给出如下代码,指出 std::forward_as_tuple() 的返回值不能被 auto 结构化绑定:

#include <tuple>

int main() {
    auto t1 = std::tuple{0};
    auto [a0] = t1;                      // ok
    auto t2 = std::forward_as_tuple(0);
    auto [a1] = t2;                      // error
}

修改代码尝试了几次发现,t2 只是不能被 auto 结构化绑定,可以被 auto &auto &&(万能引用)结构化绑定。查阅资料得知 std::forward_as_tuple() 返回的 std::tuple 的参数类型都是完美转发后的类型,t2 的类型是 std::tuple<int &&> 而不是 std::tuple<int>

将代码修改如下,果然 t1 也无法被结构化绑定了。

#include <tuple>

int main() {
-   auto t1 = std::tuple{0};
-   auto [a0] = t1;                      // ok
+   auto t1 = std::tuple<int &&>{0};     // don't use CTAD
+   auto [a0] = t1;                      // error
    auto t2 = std::forward_as_tuple(0);
    auto [a1] = t2;                      // error
}

template <typename F>
void timed_execute(std::string_view tag, F &&f) {
    auto start = std::chrono::steady_clock::now();
    f();
    auto end = std::chrono::steady_clock::now();
    auto ns = std::chrono::duration_cast<std::chrono::nanoseconds>(end - start).count();
    std::printf("%-20s: ", tag.data());
    if (ns >= 1e6) {
        std::printf("%.1fms\n", ns / 1e6);
    } else if (ns >= 1e3) {
        std::printf("%.1fus\n", ns / 1e3);
    } else {
        std::printf("%zdns\n", ns);
    }
}

用法:

timed_execute("reduce", [&] {
    auto result = reduce(v.begin(), v.end(), 0);
});

2024 年 10 月 2 日:其实 duration 的 count() 不一定非得是整数。参考 https://en.cppreference.com/w/cpp/chrono/duration ,可以自己定义数据类型为浮点数的 ratio(通用单位,在 std::chrono 中自然就表示时间单位,比如 std::milli 表示 1e-3 这个比例,而 std::chrono::milliseconds 真正表示毫秒这个时间段)。例子:

#include <chrono>
#include <iostream>
 
using namespace std::chrono_literals;
 
int main()
{
    using Minute = std::chrono::duration<double, std::ratio<60, 1>>;
    std::cout << std::chrono::duration_cast<Minute>(1s).count()
              << " minutes\n";
}
// 0.0166667 minutes

三个剪贴板

Windows 一个,X selection 一个,Wayland 一个。

安装 wl-clipboard 包可以获得 wl-copywl-paste 工具,它们操作 Wayland 剪贴板。

安装 xclip 可以获得 xclip 工具,xclipxclip -i 类似 wl-copy,完成复制功能,xclip -o 完成粘贴功能。它操作的是 X selection。

现象描述

在 Windows 上复制文本,可以写入 Wayland 剪贴板和 Windows 剪贴板,不能写入 X selection。

nvim 中选中文本,用 "+y 复制到 "+ 寄存器,可以写入 Wayland 剪贴板,不能写入 Windows 剪贴板和 X selection。

起因

同学说 Zoom 卡,影响了他在线考试。我们收集了一些资料后发现 Zoom 有中国服务器,但是只有商业用户可以连接,而免费账户只能连接到美国的服务器。我怀疑是中国到 Zoom 的美国服务器网络线路不好。

我们尝试了下发现挂代理也没用——尽管 Zoom 已经检测到了代理(在统计信息页面)。我同学说他那边丢包,我这边感觉还好,但有时候也有丢包。我担心是 UDP 请求不能被正常代理

补充:几种代理方式的能力范围

HTTP 代理

支持 IPv4 和 IPv6,仅能代理 TCP。

https://superuser.com/a/302955/ 来看,虽然其主要是代理 HTTP(当然也能代理 HTTPS),但许多 HTTP 代理服务器能够处理 CONNECT 请求(因此实际上支持了裸 TCP 连接),所以 HTTP 代理也能用来代理其他 TCP 协议。

SOCKS4 代理

支持 IPv4 的 TCP 请求。

我的主要代码是这样:

def main():
    with tempfile.TemporaryDirectory() as d:
        write_tmp_files(d)

即便是程序在终端被 ^C 终止,临时文件夹也会被正常清理,但是我发现 SIGHUP 到来时,临时文件夹就不会被正常清理了。可以注册一个信号处理器函数:

import signal
import sys

signal.signal(signal.SIGHUP, lambda signum, frame: sys.exit(0)) # 其实应该改成信号值 + 128

这样在 SIGHUP 到来时,临时目录也会被正常清理。

https://docs.docker.com/engine/security/seccomp/

seccomp 选项可以控制容器内可以执行的系统调用。可以用 --security-opt seccomp=/path/to/seccomp/profile.json,也可以用 --security-opt seccomp=unconfined 表示不受限。

This feature is available only if Docker has been built with seccomp and the kernel is configured with CONFIG_SECCOMP enabled.

之所以用这个选项,是前段时间有个要跑的服务启动失败了,和其他同类服务比较发现没有加 seccomp 选项。

对数据集的文件名正确排序

有些训练或评估程序要求数据集输入文件的顺序严格有序 1,但是文件名长度又不同,不能简单按照字典序来排序。比如,数据集的标签文件名可能是 0.txt、1.txt……10.txt 等,能想到的一个方法是在前面填充字符 '0'

lst = os.listdir(directory)
lst = sorted(lst, key=lambda s : s.zfill(50))

如果先去掉后缀名,然后再把前面的字符串转数字,则可能会遇到很多复杂的情况(比如多重后缀名),比不上在前面填充字符简单。

zfill 填充好之后,文件名都是这样的:

00000...001.txt
00000...002.txt
...
00000...100.txt