Zero 发布的文章

说到输入法当然要用开源的了,不然谁知道自己的打字记录会在云端保留多久。我在用的是 Rime 输入法+四叶草拼音输入方案+小鹤双拼,不过四叶草字库里生僻字太多,翻页翻过头时就全变成无法识别的方块了(没有安装支持过于生僻字的字体)。

字的频度值越低,说明字越生僻。所以可以设置一个最低频度来控制候选词里的生僻字数。先把频度值都取出来:

awk -F\\t '$3!="" {print $3}' data/clover.base.dict.yaml > freq.txt

数据分析用 Jupyter 笔记本比较方便。安装上 VSCode 的 Jupyter 插件,在 VSCode 里选择有 seaborn 和 ipykernel 包的 Python 环境就可以开始分析了。

首先把需要的不需要的依赖都导入进来,免得后面再找。

import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
import seaborn as sns

然后把之前提取出的词频文件用 pandas 导入

fl = pd.read_csv('freq.txt', names=['cnt'], header=None)
df = fl['cnt']

成功后就可以开始画图分析了,先画个分布图。

sns.displot(df)

然而执行了好几分钟还没跑出来结果。回头看一眼数据,最大值 579392145,最小值 0。这个分布太散直接拿来画图确实不合理,那就取个对数试试。为了后面好过滤就以 10 为底吧。

df_log = df.transform(np.log10)
sns.displot(df_log)

output1.png

这次 OK 了,可以看出个位数频度里有不少字,放大看下

df_less_than_10 = df.where(lambda x : x < 10).dropna()
sns.displot(df_less_than_10)

output2.png

三万多字集中在 1 上,回到 yaml 文件里一看全是生僻字,直接过滤了吧。

df_log_main = df_log.where(lambda x : x > 1).dropna()
sns.displot(df_log_main)

output3.png

这就好看多了。大部分字还是集中在 10e4 到 10e6 范围内。那就从频度 10000 往下翻看有没有需要保留的字。

$ cd data
$ less clover.base.dict.yaml
<用 ^I...$ 搜索定位到 1000 以下>
$ grep '扁' clover.base.dict.yaml
扁    bian    255939
扁    pian    661

看来保留到 600 字频应该就差不多了,剩下的要么是生僻字,要么是多音字很少见的读音。

diff --git a/src/clover-dict-gen b/src/clover-dict-gen
index 4ef8716..87b74a8 100755
--- a/src/clover-dict-gen
+++ b/src/clover-dict-gen
@@ -186,12 +186,13 @@ class DictGenerator:
         return (word_count, parse_count)
 
 
-    def getWordDictText(self):
+    def getWordDictText(self, min_freq = -1):
         """
             生成单字的 rime 字典文本
         """
         # 按频率倒序排序
         word_list = [(key[0], key[1], self.word_dict[key]) for key in self.word_dict]
+        word_list = list(filter(lambda w: w[2] > min_freq, word_list))
         word_list.sort(key = lambda w: w[2], reverse = True)
 
         # 生成文本
@@ -292,7 +293,7 @@ name: %s
 version: "1.0.0"
 sort: by_weight
 ...
-""" % word_dict_name + generator.getWordDictText()
+""" % word_dict_name + generator.getWordDictText(600)
 
     parse_dict_text = """
 # Rime 字典

搞定,重新 build 一下就好了。

目前搭建本地 k8s 集群的方案还挺多的,有官方的 minikube,也有第三方的 k3sk3dKubeKey 等等。简单的测试其实用 k3s 搭个单节点测试就好,但遇到有反亲和规则的 Helm chart 就拉不起来了,所以要么多弄几台机器做分布式集群,要么就用套娃式的虚拟化方案做出多节点来。

现在手头就一台高配物理机,套娃就套娃吧。一开始看 minikube 在 https://github.com/kubernetes 下应该很靠谱才对,但是一个 MySQL 数据库都没拉起来,看报错是文件权限问题。不过 bitnami 的 chart 文档里已经给出了解决方案,启用 volumePermissions.enabled 就可以自动添加一个初始化容器执行 chmod。看起来还行,我把相关子 chart 都启用了这个配置,helm install 也成功了。可这 chart 它不讲武德,竟然在最后的 init 容器里又执行了一次 helm install,比我还能套娃。

这套娃 chart 代码在容器里不是很好改,还是老老实实看怎么让 fsGroup 生效吧。谷歌一下找到 #1990 (comment) 这个 issue,k8s 的 hostPath PV 实现不支持用 fsGroup 配置权限,minikube 和 k3d (rancher/local-path-provisioner#41) 同时中招。

查了好久也没找到合适的方案,看来只能试试非正当手段了。能不能在 hostPath 创建好目录之后,马上把权限改对呢?脏是脏了点,但能抓住老鼠就是好猫。正好 k3d 创建集群时能指定 --volume,直接把宿主机上的目录 mount 进去,然后 for 循环 chmod,总算是顺利把套娃的 helm install 也搞定了。

后记

全都搞定之后才发现 OpenEBS 似乎能解决问题,KubeKey All-in-One 模式也能一键安装,不过懒得折腾了。

后来还发现 minikube 提供的 StorageClass 还不支持多节点集群 (#12165),官方这么惨大家快去欺负(帮忙)。

背景

Fluent Bit 在代码中有将时间转化为浮点数(单位秒)再和 0.0 对比判断时间是否为 flb_time_zero 的初始值。在这个场景下用整型运算应该更快,但在提交 PR 前还是需要用数据说话,所以先来 benchmark 一下。

环境准备

这次我使用了 Google 维护的 benchmark 库,使用 Conan 管理依赖并用 CMake 构建。先安装一下:

sudo port install pipx cmake
pipx install conan

然后准备 conanfile.txtCMakeLists.txt,可以参考我的仓库 benchmarks

benchmark 单元编写

static void BM_Nano(benchmark::State& state) {
    struct flb_time out_time = {0};
    flb_time_zero(&out_time);
    bool flag;
    for (auto _ : state) {
        flag = flb_time_to_nanosec(&out_time) == 0L;
    }
}

只要把需要测试的代码放到 for 循环内就会自动测试这段代码的性能。但测试结果出来后有点奇怪:

-----------------------------------------------------
Benchmark           Time             CPU   Iterations
-----------------------------------------------------
BM_Nano         0.249 ns        0.249 ns   1000000000
BM_Sec          0.248 ns        0.248 ns   1000000000

运行了几次时间上都差不多,难道现代 CPU 的浮点性能这么强了?感觉有点不对劲,试着加上一个 BM_None 测试做对比,代码里直接赋值 true 不做任何运算:

for (auto _ : state) {
    flag = true;
}

测试出来结果和其他两个都相同,数据肯定是有问题了。

排查原因

打开 Compiler Explorer,添加 benchmark 库,一看编译出的汇编就明白了。

bm1.png

整个 for 循环都被优化掉了。。。

解决方案

在网上搜索如何阻止编译器优化某部分代码,发现最简单且相对通用的方式是使用 volatile 关键字,那给 flag 变量加上 volatile 试试。

bm2.png

汇编变长了,但还是没生成浮点运算。看来还不够 volatile,再加到函数参数上试试:

bm3.png

总算看到要 benchmark 的函数被上色了!再跑一遍 benchmark 试试?

-----------------------------------------------------
Benchmark           Time             CPU   Iterations
-----------------------------------------------------
BM_None         0.242 ns        0.242 ns   1000000000
BM_Nano         0.372 ns        0.371 ns   1000000000
BM_Sec           1.11 ns         1.11 ns    623846997

果然如我所料,浮点运算的性能还是要差一些的,于是提交 PR #4535,搞定收工。

试着使用 MacPorts 提供的 cmake 命令从源码编译 Krita,但编译依赖就出现了 include、链接缺少符号等问题。原因是 MacPorts 的 cmake 包有个 patch,会优先从 MacPorts 的 prefix 下查找。

--- Modules/Platform/Darwin.cmake.orig
+++ Modules/Platform/Darwin.cmake
@@ -237,7 +237,9 @@
     endforeach()
   endif()
 endif()
-list(APPEND CMAKE_SYSTEM_PREFIX_PATH
-  /sw        # Fink
-  /opt/local # MacPorts
-  )
+
+# prepend the MacPorts prefix to CMAKE_SYSTEM_PREFIX_PATH
+# might already exist after this if the user has specified it
+list(INSERT CMAKE_SYSTEM_PREFIX_PATH 0
+  __PREFIX__
+)

无奈之下只能自己编译一个 cmake 了。如果你的 MacPorts 使用了默认的 /opt/local 作为 prefix,需要在解压缩 cmake 源码后修改 Darwin.cmake 去掉这行 /opt/local # MacPorts 再安装。

qt.qpa.plugin: Could not find the Qt platform plugin "cocoa" in ""
This application failed to start because no Qt platform plugin could be initialized. Reinstalling the application may fix this problem.

这是由于 MacPorts 的 qt5 不适用普通 macdeployqt 命令,打包时去掉这个命令就能正常启动了。