kuanat

kuanat

V2EX 第 634702 号会员,加入于 2023-06-19 11:38:40 +08:00
今日活跃度排名 5558
kuanat 最近回复了
115 天前
回复了 june4 创建的主题 编辑器 未来最牛编辑器 zed 的 Linux 版终于出来了
@lysShub #71

没有说怎么测的,我也没细看,就假定它用的一致的测试方法好了。延迟测试包含了语法分析,但是因为它只是在新的一行里反复按了 10 次 z ,所以这个语法分析的时间可以忽略不计了。
116 天前
回复了 june4 创建的主题 编辑器 未来最牛编辑器 zed 的 Linux 版终于出来了
我这里简单就 Zed 官网宣传的一些特性简单做个总结,同时与 VS Code 对应的部分做个对比。


1. 输入延迟

Zed 在性能方面有个对比,强调的是 Insertion latency ,58ms 相对 VS Code 97ms 算是非常优秀了。这个测试反映的是输入状态下按下按键,到字符出现在屏幕上的时间,如果你玩游戏的话,很容易理解这个就是所谓的输入延迟。

你可能觉得 40ms 影响不大,但这个特性属于有对比才有伤害、或者说像高刷屏用了就回不去那种。不仅仅是在文本编辑器领域,像终端( vte )领域一样对于输入延迟有追求,所以你会看到像 kitty/alacritty 这样通过 GPU 加速绘制的终端模拟器。再比如很多人会觉得 oh-my-zsh 之类的框架会拖慢 shell 的启动,考虑换成 fish 之类作为替代。

2. 渲染模式

也许你会认为 Zed 渲染快延迟小是得益于原生应用相对于 electron 的优势,是有这方面的原因,但是不够全面。同样是输入延迟部分,Sublime Text 4 大概是 75ms ,可以粗略的认为,Sublime 相对 VS Code 的优势来源于原生,但 Zed 相对于 Sublime 的优势有一部分是来自于渲染模式。

包括各种终端模拟器在内的应用,所谓的 GPU 加速更多的是使用 immediate 模式,相对于 retained 模式。前者就是把应用当作 3D 游戏,自己负责渲染每一帧。后者就是一般意义上的原生应用,使用系统控件,只有在状态发生变化时才通知系统,由系统负责渲染。

Zed 使用的是 Vulkan 后端,其 UI 界面就是基于 immediate 模式自渲染的。(技术上说,应用 GPU 加速通常更快,但不总是更快,这一点放到后面说)

3. 语法分析

如果增加一个 Deletion latency 的测试,我相信 Zed 会胜出更多的。原因是无论插入还是删除,后台都要做语法分析,从而提供高亮、补全等等功能。删除相比插入更能反应语法分析器的性能,特别是跨多行或者代码存在语法错误的时候。

Zed 的开发团队的前身就是做 Atom 的团队,而 Atom 当年之所以有底气做编辑器,是因为他们写了 Tree-sitter 这个通用的 parser 生成器。现在 Tree-sitter 是事实上最通用、也是效率最高的语法解析器的“生成器”。不是 parser 而是 parser 的生成器,这一点尤为重要。

在 Tree-sitter 之前,通用的语法解析主要是 TextMate 的,后续包括 VS Code 在内也都沿用了下来。连 VS Code 的开发都说,用 TextMate 这套的原因就是不用为每一门语言都写规则,然后他们也发现很多语言的规则都多年没人维护了。当初 TextMate 设计这个功能的时候想的是,尽可能在不用写 parser 的情况下完成 parser 的主要功能,所以实现方式是正则匹配。放到二十年后的今天来看,这个做法就跟不上时代了。

一方面现在的性能和软件实践来说,写 parser 和实时处理都相对简化了,既然都能完整实现和运行 parser 了,那就没必要用弱化版的了。另一方面,基于正则的弱 parser 实现只能处理一行,也不能支持像是“增量”处理,或者在代码被修改乱了,存在语法错误的时候还能正确输出尽可能有用的信息。

我个人认为,Tree-sitter 在这方面对于写代码的改善是非常明显的,不仅速度快结果还准确,特别是错误状态下依然有用这点十分好用。想要尝试的话可以用 neovim 或者其他基于 Tree-sitter 的编辑器感受一下。

4. 协同

现在没有什么编辑器能做到像 VS Code 的 Remote 功能一样彻底颠覆开发的工作流。即便 JetBrains 这样也算财大气粗的专业开发,照着现有的方案模仿都做得一塌糊涂。所以也不要对 Zed 有什么不切实际的期待。

Zed 目前能做到的是还是本机做 host ,然后可以编辑 remote 的文件而已,对用户隐藏了拉取和推送文件的细节。

所以 Zed 就专注做了协同功能,在本地编辑器上实现了多人实时编辑同一文档的在线编辑器体验。这个功能基于 CRDT 思路,有兴趣可以去看看。



关于“GPU 渲染为什么不会总是更快”这个说法的解释:

一般来说,GPU 渲染会更快一点,但就文本编辑器(或者终端 vte )这个场景来说,GPU 渲染不一定比 CPU 更快。这是因为 GPU 更快是建立在全渲染的前提之上的,而文本编辑这个场景很多时间并不需要全渲染。

比如输入的时候,发生变化的一般只有当前行。浏览代码的时候,代码内容不变只是显示位置变了。这样的场景里,使用 damage tracking 类算法,就是类似 vnc/xdp 那种检测改变部分的算法,可以极大降低渲染的工作量。以游戏渲染的思路来看,无非就是 framebuffer 的手动控制更新。

所以我个人并不喜欢使用 GPU 渲染的文本编辑器或者终端模拟器。当然,如果它像浏览器那样,将字体渲染过程中的处理交由 GPU 完成,我还是很支持的。
116 天前
回复了 june4 创建的主题 编辑器 未来最牛编辑器 zed 的 Linux 版终于出来了
虽然“未来最牛”这个说法值得商榷,Zed 作为一个编辑器还是有很多亮点的。目前来说想挑战 VS Code 的生态地位不太可能,但作为一个有潜力的(部分)替代说句未来可期不过分。至少从执行力上(当初说要做就真做了),还有第一个算是 public beta 的成品质量都是很靠谱的。

晚一点我写一下 Zed 相对比较优秀的特性,然后和 VS Code 做个简单的对比。

另外 Linux 用户应该不用担心中文输入的问题,目前只有 sway 存在不显示候选词的 bug ,其他支持 text_input_v3 的主流桌面都正常。sway 主要是维护上游 wlroots 所以发版慢,实际上自身早就支持了。
119 天前
回复了 barathrum 创建的主题 NAS 到底还是 all in boom 了
尴尬,总是一半就发出去……

如果非常在意的话,可以借鉴风险管理的思维,你能承受的损失有多大,能够接受的恢复周期有多久,由此来制定 rto rpo 策略,进而指导架构的方针。

举个例子,比如我不能接受 all in boom 带来的断网,那就把网络单独拿出来,做个硬件备份,以应对和切换。如果是不能接受存储文件的损失,那就把存储后端独立出来。凡是没有高可用需求的大可放心地集成到一起。
119 天前
回复了 barathrum 创建的主题 NAS 到底还是 all in boom 了
如果非常在意的话,可以借鉴风险管理的思维,你能承受的损失有多大,能够接受的恢复周期有多久,由此来制定 rto rpo 策略,进而指导架构的方针。

举个例子,比如我不能接受 all in boom 带来的断网,那就把网络
119 天前
回复了 barathrum 创建的主题 NAS 到底还是 all in boom 了
备份是个小事情,能不能从备份中恢复才是大事情。
这一套跑起来要多少内存属于常识性问题,不是说那遇到过才知道的。另外企业级代码也要有企业级的运行环境做支撑啊,没有配套的环境谈什么企业级。

不如反过来你尝试回答这样一个问题,这个需求你还能用别的方式熟练的完成么?不能的话那你没得选,能的话说明决策有问题。

比如这个需求 mysql 换成 sqlite 不行么,裸写 sql 不行么,甚至说存自定义格式不行么?既然都是 http 请求加 json 解析,换成 Python 不行么,如果 python 不方便部署,换成 go 之类的不行么。

特别当你知道运行环境就是 1c1g 的时候,更应该考虑这些变通的手段啊。如果换成我的话这个需求我连 self host 都不会用,免费的 cf worker 比你的小主机可用性高太多了。也许你都没考虑过长期维护的事情。这种需求要的是省心,但你现在的选型和实施都没自动化的考量,配置一个简单的 cd/ci 之后这个差距会更大。

说这么多其实是想表达,不要把完成需求仅仅局限在写代码的层面上,当成项目来管理运营会更容易做出整体上更合理的决策。
123 天前
回复了 d0x0b 创建的主题 程序员 我至今仍感到羞愧的代码
大概十多年前约 2013 年前后,我写过很多用于设备追踪和用户识别相关的代码,那个时候收集用户隐私几乎是稀松平常的存在。现在相关的技术一般叫做指纹。

如果现在让我评价,我认为这些代码属于作恶性质的。丝毫没有对用户隐私的尊重,收集的信息数据最终都变成了商品。我有的时候会宽慰自己,即使我不做也有别人去做。这么说确实没错,但是放到十多年后的今天,别人可能只会抱怨环境恶劣,而作为曾经参与其中并推波助澜的一员,我会有种非常微妙的感受,就是恶心别人到头来终究恶心到了自己。

我个人认为在当时我这里“研发”的一些技术属于思想和实现都比较领先的,甚至有些技术在十年后依旧被广泛应用。

随便举个例子,当时大概是 iOS 7 的样子,我这里就在使用 url scheme 去判断用户安装了哪些应用。当然系统是不会提供这样的 api 接口了,让应用可以直接查询到哪些 url scheme 被注册。于是我就把当时软件商店按下载量拉回来主流应用并解包,获得相关的 url scheme 。通过这样的方式,可以在静默的状态下获得已知应用(有注册 url scheme )列表中应用的安装状态。一两年之后,iOS 才对访问 url scheme 的行为增加 UI 提示。

这个方法看起来很粗糙,但是它的思想是很深刻的,实际上行业内普遍应用都是好几年之后的事情了。甚至同样的技术手段,三年前还在用于桌面浏览器的指纹识别,桌面浏览器封堵相关漏洞也是很晚的事情。

说它粗糙是因为在十多年前,使用 url scheme 的应用数量有限,即便如此,十个应用即可获得 10bit 信息,16bit 就足够识别 65536 个用户了,这在当时已经超过了大多数用广告 sdk 的客户的用户数量。(实际的有效信息量会有损失,因为像微信支付宝这样的国民应用基本不具有可辨识度)

说它深刻是因为现如今所有的指纹技术,核心思想都是通过多渠道手段,采集在统计意义上独立的特征信息,大概 32bit 在实际应用中足够非常精准的识别了。当然现如今 app 根本不用这么麻烦,因为它们几乎都是随意采集。
主要问题是这个报错 `RPM: error: Unable to change root directory: Permission denied` 因为 dnf 是脚本,最终还要调用 rpm 的。dnf 的参数 `--installroot` 根据文档的描述是类似 chroot 之后 dnf 的,这个过程里 dnf 提前设置一下 rootdir 用到的配置文件等等。这个 chroot 的操作实际上是由 rpm 完成的。上面的报错是说 rpm 没有获得 CAP_SYS_CHROOT 这个权限导致的。(另外实际安装的时候也应该也需要 CAP_CHOWN 权限,也就是说单独给 chroot 权限不够,不过 chown 权限比较复杂,后面说)

同样因为 dnf 是个脚本,所以它只是机械地检查自身是否为 root 来判断自己能否执行。所以在不改动 dnf 的前提下,只能把 dnf 放到 root/uid0 去执行(不管是不是真的)。

fakeroot 的原理是通过 LD_PRELOAD 拦截并替换需要 root 权限的 syscall ,让被调用的程序认为自己是 root 。#9 失败的原因我猜测是 dnf 是个脚本,如果是 subprocess 的方式去调用 rpm ,那么 rpm 不会实际获得 chroot 权限。

顺便说一下 fuse 的原理,它是用户空间的实现,如果用 `-o fakeroot` 的话,相当于这个文件系统还是 ext4 ,原本所有操作都应该检查 ext4 里面的权限,开启 fakeroot 之后会强制可读可写,即 RWX 那一套不起控制作用。虽然名字也是 fakeroot 但不是一个实现方式。

所以理论上,只需要运行 `unshare -m -r dnf ...` 就可以创建一个 ns 让 dnf 以 root 权限运行,`-m` 创建 ns ,`-r` 将 ns 内的 uid 映射为 root 。

我个人不建议用上面的方式来运行,因为 `unshare -m -r dnf ...` 执行的是宿主机的 dnf 所以你需要传递一些 dnf 相关的参数,尽管 dnf 是做了很多工作,但还是有可能出问题。我更建议的方式是 `unshare -m -r chroot /path/to/fedora_rootfs dnf ...` 这样直接 chroot 进虚拟机系统,然后用虚拟机的 `dnf` 完成包管理。

另外实际上这样做还是太粗糙了,有几个问题:

- ns 里面环境变量不一定正确
- 一些特殊的 /proc 之类的文件系统不会绑定
- chown 需要额外的 uid/gid 映射表才能正常工作

所以一般会用 `podman unshare` 来解决上面这些麻烦。这个命令的实现来自于 `buildah unshare`,buildah 是 RH 系的构建系统,podman 也是 RH 开发的,所以就把这个功能用同样的方式实现了。

之所以要用 `podman unshare` 而不是直接用 `unshare` 是因为你自己处理 uid/gid 映射是比较麻烦且容易出错的。而 chown 的调用在 rpm 包中很常见,rpm 在安装包的时候会执行创建用户/组以及更改权限等相关的脚本。

当以非特权用户执行 unshare 的时候,只能够映射自身 uid 或者将自身映射为 fakeroot 。这个设计的初衷是防止 ns 内部冒充宿主的 root 以避免被漏洞提权。实现方式也比较简单粗暴,比如宿主机上 uid 范围是 0~65535 那么 ns 里面就是 100000~165535 这样,ns 里面的 0 对应的是宿主机上的用户。

所以实际上要满足 ns 中支持多 uid/gid ,那么宿主机上普通用户权限是不够的,但是又不希望以 root 权限创建 ns ,于是就通过具有 CAP_SETUID 权限的 /usr/bin/newuidmap 来实现。如果你要自己手动通过 `unshare` 来完成上述工作,就需要借助 newuidmap/newgidmap 。
131 天前
回复了 gesse 创建的主题 程序员 讨论一个 UDP 问题,关于监听。
如果是 connected 可以通过 UDPConn.LocalAddr() 获得,如果是 unconnected 要通过 ReadMsgUDP 的 oob 信息获取,底层实现应该还是 IP_PKTINFO 。

如果嫌麻烦可以每个 ip 对应一个实例或者 goroutine ,这样就知道本地绑定的 ip 了。
关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   实用小工具   ·   5706 人在线   最高记录 6679   ·     Select Language
创意工作者们的社区
World is powered by solitude
VERSION: 3.9.8.5 · 16ms · UTC 03:20 · PVG 11:20 · LAX 19:20 · JFK 22:20
Developed with CodeLauncher
♥ Do have faith in what you're doing.