V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
V2EX  ›  kuanat  ›  全部回复第 7 页 / 共 13 页
回复总数  244
1  2  3  4  5  6  7  8  9  10 ... 13  
231 天前
回复了 afxcn 创建的主题 Go 编程语言 golang 的 defer 真是个好设计
@Jirajine #62

我的观点一直都是,在合适的场景使用恰当的工具。在说 Go 好的帖子里这样说,在说 Go 不好的帖子里也这样说。就连这个帖子里,我的观点也是一致的,defer 不能滥用,而 error 处理要换思路。

关于 sum type 我从来没谈过,因为我想不出这样做的原因,我也没有从 Go 的官方文章找到头绪。Null safety/checked initialization 都是用来佐证,语言特性是取舍这一观点的。既然选择了简洁,就要付出代价,Go 没有特别多的选择。

既然你能准确使用这些术语,说明对这些话题有足够的认知,那完全可以阐述你对于语言设计的认识和观点,没有必要归结于个人偏好。
@sakujo #13

不开弹幕的话,就是单纯的扣一块区域,调用硬件解码器。浏览器本身是不负责播放区域的,系统窗口合成器把浏览器渲染的其他部分与视频解码的播放部分进行叠加。这个过程里,显卡的 Render/3D 单元的占用取决于视频是否在播放。暂停的话占用率会立即下降。

如果开启弹幕,弹幕区域等价于在跑一个 3D 应用,这个应用就是在播放区域之上渲染一个透明层,然后把弹幕显示上去,一般这个 3D 应用的帧率会和屏幕刷新率一致。即使视频暂停,这个透明层依旧不会停止运行。(视频暂停会看到旧弹幕飘过,只是不加载新弹幕了。)

CPU 要负责生成弹幕的帧,同时一些类似于避开视频中的人物会加大计算量,因为这个功能实际上要抓取画面,然后计算分析生成绕开人物的蒙版。

风扇转是因为 cpu gpu 都在高负荷工作。手机这个功耗水平都能完成的事情,只要硬件解码正常工作,没有理由电脑还很吃力。
232 天前
回复了 t41372 创建的主题 操作系统 很讨厌桌面端系统的权限管理模式
追加提一句作为我 #9 回复的补充。

systemd 提供了一个 sudo 的替代品叫 run0 ,今天刚发布,某种成都上是解决 sudo 是为传统多用户设计,不适合现代个人 PC 场景问题的新思路。(当然要实现沙盒化的权限机制,软件适配还是无法避免的。)

技术上 sudo 是 SUID 实现的,除去安全问题之外(任何人都可以执行 SUID 应用,所以额外需要 sudoers 配置文件),这套权限体系是不区分人和应用程序的。这就造成人控制硬件设备还要 sudo 非常麻烦,而应用获得权限反倒可以为所欲为的现状。

run0 不依赖 SUID ,而是通过 PID 1 来 fork 出一个隔离的 pty 执行相关命令和应用,权限部分由 polkit 控制。

好处就是大部分之前依赖 sudo 的硬件控制功能,都可以在已登录用户 session 的情况下无感授权。尽管还做不到像手机上那样基于隐私的权限体系,但已经可以把相对危险的操作隔离开了。

只是目前看它还是 systemd 的一部分,想要完全替代 sudo 还需要时间来验证。
232 天前
回复了 afxcn 创建的主题 Go 编程语言 golang 的 defer 真是个好设计
我看到楼上有人提多返回值的类型问题,这里也多解释一下。当然我觉得有必要再重复一次:

- 不要拿 Java/C++ 的思路去套 Go 的实现
- 不要单独讨论一个特性点的好坏

异常处理并不是只有 try...catch 这一种范式。Go 之所以设计成这个样子就是为了干掉 try...catch 。Go 这样做单独和其他语言放到一起比显得很笨,但却是非常符合 Go 需求的方式。


现实世界里“逐级汇报”和“越级处理”都是普遍存在的。习惯了随意抛出,集中处理,要是异常的类型不确定,肯定会让人抓狂,而且还会觉得,有事说事,明明没问题还汇报个屁。这样想下来,肯定觉得 Go 哪哪都是毛病。

现在换一种眼光,如果用逐级汇报的想法。Go 就明说了,我的机制比较死板,只能逐级汇报,而且模式都是没问题就过,有问题就汇报问题。这样能处理的原地就处理了,不能处理的考虑继续交给上级,直到有人能处理为止。

对 Go 来说还有个问题,要是不止一级出问题怎么办?总不能无限追加返回值的数量吧。为了解决这个问题,Go 把 error 定义成了接口:type error interface { Error() string }。

这里有两层用意,一是中间层级可以追加或修改错误内容,二是指明了 Errors are values 。第一点映射到现实就是,如果中间层级也出错,那就追加进去,甚至可以做请示备注。(这里不抬杠,C++/Java 也可以逐级处理)


Go 真正创新的地方在与第二点:错误是个值,而不是个类型。一旦明白错误是个值,你就会发现异常处理不再强依赖 goto 类控制流程。同时错误状态和错误本身也完成了解耦,控制逻辑只需要判断有没有错误,至于错误,是否处理、由谁处理甚至什么时候处理都有完全的自由。

所有纠结 Go 返回值错误类型的,有没有思考过,凭什么错误一定要是一个类型,这种先入为主的概念是从哪里来的?这就是我一直反复强调的,千万不要手里拿把锤子,就看什么都是钉子。
232 天前
回复了 afxcn 创建的主题 Go 编程语言 golang 的 defer 真是个好设计
最后补充一下我理解的 Go 为什么不选择 RAII 路线的理由,runtime 原因之外的理由。

Go Proverbs 里面有一句 Clearer is better than clever ,说得更直白一些就是 explicit over implicit 即显式优于隐式。

Go 的设计目标“合作”还有工程上的意义,鼓励程序员之间以“合作”的思维来写代码。这一点我在某个讨论包管理的话题里简单提到过一次。

不夸张地说,Go 是我接触过的所有编程语言里,读代码最容易的,没有之一。不管这个 Go 代码是 Java 味道或是什么别的。

原因我猜测是在于 Go 没有隐式的调用。但是基于 RAII 的实现就会存在问题,构造和解析会触发预期之外的代码执行,作为代码或者项目的使用者就要主动去注意相关的问题。

于是 Defer/Panic/Recover 这一类影响控制流的功能都在语言层面上做了限制,这类隐式 goto 的行为也是可预期的,代码也通常不会间隔太远。基于同样的理由,Go 不推荐在无必要的时候在包层面上使用 Init 方法。

与之相关的是,Go 对于零值的执着。一方面它是作为弥补没有构造机制导致表达力缺失的手段,另一方面它是让开发者重新思考构造、默认值和 sane defaults 这些问题。

尽管有人会吐槽 Go 的 pkg.dev.go 被低质量的包污染(也许说得是好名字被提前占了?),但可能没意识到 Go 的简洁带来的好处,一个包的质量好不好,看下代码几分钟就能弄清楚。

PS

说到底,机器是没有办法完美 GC 的,如果人人都能写出生命周期管理完美的代码,也用不到机器了。在这个意义上,defer 提供了非常简单的机制辅助开发者,我认为是语言设计层面上非常大的思路进步,这个机制只有足够简单才能让人有欲望去用。
232 天前
回复了 afxcn 创建的主题 Go 编程语言 golang 的 defer 真是个好设计
回复比较长,简单分了几节方便阅读。

A.
虽然我很对于 Go 设计层面的评价是比较高的,但讨论这种话题还是先表明立场比较好……

1. 语言的单一特性不适合拿出来单独对比,每种语言都是在各种特性中做取舍,单一特性有可能是被偏爱的那个,也有可能是被牺牲的那个,但都是服务于语言整体的。

2. 语言提供的功能特性有适用范围的,有不代表一定要用,好不好用看场景。千万不要手里拿着锤子,看什么都是钉子。

3. 不要用一种语言的思维去套另一种语言的设计。也就是常说的 XXX 味道的 YYY 代码。


B.
回到 defer 设计的问题上,要知道 Go 在关键词上是异常吝啬的,那为什么要专门设计 defer 这样一个看似作用不大的语句?假如没有 defer 语句,对现在的 Go 影响大吗?

一旦你意识到这个问题,越往下深挖就越能理解“妥协”在语言设计里的意义。以下的部分都是我根据记忆总结的,大多数来源是官方文档和一些开发者的个人 blog ,然后形成了我自己的理解,所以不一定正确,大家谨慎参考。


我不止一次在站里各种 Golang 相关的帖子里提到过,Go 语言设计核心的要素是合作,换句话就是妥协。有些事情程序员做很麻烦,那就让语言或者编译器多工作一点,有些事情编译器很难优化,那就让程序员多担待一点。

合作体现最明显的地方是 goroutine 。过去 Linux 在非常长的时间 IPC 信号机制尽管有类似的尝试,但缺乏合作意识导致真正其作用的只有接收方无感知的 SIGKILL/SIGSTOP ,而 SIGTERM 之类的 graceful 操作应用很少。而 Golang channal 机制是内存模型层面唯一保证线程安全的手段,无形中迫使开发者尽可能写出合作式的多线程调度代码。

同时 Golang 设计目标中优先级很高的一点是概念和语法层面的简洁。但是一般来说,写起来越简单,编译器就越复杂。核心设计者在早期就一边写编译器,然后他们发现,try...catch 会使得编译器产生大量难以优化的 goto ,而 RAII 的方式又会使得 runtime 变得复杂。

所以最终的妥协是:支持精简的 runtime ,放弃 RAII 的析构;使用基于错误返回的异常处理,放弃 try...catch 语法。同时还包括使用 GC 降低开发者心智负担,而不是 Rust 那样为了安全增加大量程序员的工作。这里还是需要强调一遍,这些设计抉择是为语言整体服务的,不能单独拿出来比较好坏。


这样做的结果是:

- Golang 中对于 OO 的支持是基于接口的 duck typing 模式,并没有 RAII 支持。
- Golang 没有复杂的控制流结构,只能通过序列式返回错误。程序员被迫要写大量 if err!= nil {}。

相应的好处也显而易见,Go 在很多时候有着媲美无 GC 语言的运行效率,以及非常高的开发效率。

其实在 Go 设计者眼里,语言表达能力弱是可以接受的妥协,而且对于程序员来说付出的代价能够接受。反过来为了开发者习惯做妥协,牺牲编译器效率和 runtime 简洁性就是无法承受之痛了。


C.
在聊 defer 之前,还有点铺垫要做。不清楚开发者们有没有意识到,官方文档对于 defer 讲解的文章标题是 Defer, Panic, and Recover 。

Go Proverbs 里专门有一条 Don't Panic 。意思是不要用 try..catch 的思路来同时处理异常和错误,因为 Go 没有 RAII 。具有 C++/Java 背景的开发者会非常习惯用相同的控制流去一并理异常和错误,而且处理的位置是调用栈的最外层,但 Golang 要求开发者转变思路了。

于是官方文档里 Golang 要求开发者重新审视:什么是错误( error ),什么是 Panic ?一般意义上,可以恢复(即能够继续运行)的异常叫错误,而无法恢复的异常叫 Panic 。

为了弥补缺少了 try...catch 结构导致的表达能力弱的问题,即区分错误类型的问题,Golang 顺利成章地借鉴了多返回值的设计思路。但是依旧不那么便利,毕竟 try...catch 结构里的错误类型是可以在其他地方定义然后使用的。于是 Go 更进一步,将 error 的类型定义为接口。尽管不是那么完美,至少能用了不是。

这里我想再重复一遍,当了解清楚 Golang 设计思路之后,你认为还能单独拿 if err != nil {} 来评价 Golang 吗?


D.
回到上个问题 Defer/Panic/Recover 放在一起是因为这三者的作用域都是当前 function ,这是和传统 RAII 控制流不同的。

官方怕被误用,给出了相关的实现逻辑:Panic/Recover 都是内置函数,defer 是基于栈的 LIFO 队列。所以需要使用者注意循环嵌套的问题。

本质上,defer 作为语法糖的用途只是个副作用,它的设计用途其实是为 Recover 服务的。Recover 只有在 defer 调用时才有意义。我这里引用一下原文:Recover is a built-in function that regains control of a panicking goroutine. Recover is only useful inside deferred functions. During normal execution, a call to recover will return nil and have no other effect. If the current goroutine is panicking, a call to recover will capture the value given to panic and resume normal execution.

说得再简洁一些,Panic 是个隐式 goto ,通过 defer recover 可以利用 defer 的栈结构简单回到之前的执行位置。

这也是官方说 Don't Panic 的原因,没有必要就不需要用。从哲学层面区分错误和 Panic 可以简化逻辑模型。


E.
最后回到更加原始的问题上:编程语言设计大 runtime 和 GC 的目的是什么?还不是因为手动内存、对象管理对开发者造成的心智负担太重了。那让开发者养成“谁污染谁治理”的习惯不好吗?这可太难了,随着控制结构变得复杂,创建和释放资源的位置可能隔着十万八千里呢……

所以回头看 defer 作为语法糖的副作用,栈实现和作用域限制了它的应用范围,了解清楚这一点就更容易明白,defer 不是银弹,该手动的时候还是要手动。

就以楼上的例子:循环里加锁、defer 解锁就是非常不合适的用例。不仅仅是因为 defer 的作用域在锁上会扩大 critical section ,更重要的是违反了创建者负责释放的原则。

我这里有个非常简单的原则:在 function 作用域内即可完成的资源创建和释放,不用 defer ;创建资源之后需要给其他 function 调用,在创建之后 defer 释放。


希望我这一大长篇能够说清楚这个问题。考虑到以上都是我个人的理解,难免会有误解或者错误,欢迎各位斧正。
233 天前
回复了 kuanat 创建的主题 V2EX V2EX 是否会考虑增加专栏功能?
@shendaowu #4

试了一下确实稍微有点麻烦,我思考了一下,可能站长说的 IPFS 分发的方案也可行。或者等主题多了做个汇总主题。
如果 Windows 有这个功能,大概率可以 Linux 实现。难易程度主要看硬件实现的方式,我简单描述下方法,你可以先尝试一下。



一般来说,电池都会通过 ACPI 提供像电量、充电状态信息等等接口,然后通过 sysfs 抽象供用户空间的应用使用。

所以第一步是看 /sys/class/power_supply 的设备,笔记本上 power_supply 类型一般有电源和电池。

能找到对应的电池的话,可以顺着 /sys/class/power_supply/XXX 链接去找对应的电池设备,看看有没有类似 charge_control_end_threshold 这类名字的抽象,这个名字都是厂家自己定义的,但是看字面意义一般就能猜出来。

如果存在的话,可以直接尝试写入。通常电池还会有另一个属性 status ,取值大概是 full/unknown/charging/discharging/not charging ,这个属性几乎都是一样的。not charging 就代表外接电源通电但电池未充电,可以据此判断充电限制是否生效。

还有一种可能是并没有一个 threshold 之类的属性可供设置,但有个类似 limit/switch 命名的属性,代表是否开启充电。通过写 0/1 来测试是否生效。



上面说的都是比较理想的情况。不太理想的情况是,这个充电控制是 EC 完成的,并没有暴露在 sysfs 里面。

那就需要你去 Windows 里,使用 https://github.com/hirschmann/nbfc 这个项目里的 ec_probe 工具,用来检测 EC 寄存器的变化。通过切换软件的充电控制开关,观察 EC 寄存器的变化,用控制变量的方法最终确定相应的地址,然后尝试改写寄存器的值,判断充电控制是否生效。

如果能找到对应的地址和值,用 ec_probe 的 Linux 版本做相应操作即可。(如果开启 SafeBoot 的话,Linux 会进入 Kernel Lockdown 模式,不允许修改 MSR 的值,这就需要 https://github.com/musikid/acpi_ec 的内核模块来修改)



如果 EC 接口也没用,那大概率这个功能是 WMI 驱动完成的,这就需要把 Windows 版的控制程序拿来逆向,然后改写成 Linux 可用的内核驱动模块。这个方法比较复杂就不多说了,如果真遇到了可以再聊。
233 天前
回复了 kkocdko 创建的主题 Linux Linux 笔电的所谓省电技巧
@Donduck #59

我用 Windows 很少,不确定是不是因为 AMD 给 Windows 的调度驱动更完善一些。论方便的话 Linux 发挥一下想象力,有各种实现方式。

基于焦点的切换(不仅仅是游戏模式)我之前在 Linux 尝试过,个人感觉不太好用,因为我一般是后台编译 rust 的时候就切走了。实现也比较简单,sway IPC 监控一下焦点切换消息就好了。

现在用得最多的是快捷键切换,然后状态栏上有个图标响应当前状态。

这个功能比较“标准”的做法是模仿 inhibitor 的模式,写个 loader 脚本,用 loader 启动想要高性能运行的应用,应用结束了 loader 退出也就结束高性能状态。
楼上说得很对,ffmpeg 几乎大部分 filter 都有附带的 scene detection ,这是一个效率优先的实现,需要根据目标视频反复测试参数阈值。

原理是以 YUV 信号提取帧的 luma 亮度信息,速度虽然快,但主要是适应动、静态的场景转换。对于比如讲座类视频效果就不好。

达到目标还有很多思路,比如相似性 hash ,点云等等,主要看目标视频的特性。
233 天前
回复了 t41372 创建的主题 操作系统 很讨厌桌面端系统的权限管理模式
这事还真的是历史包袱,而且不推翻重来的话,看不到什么改善的空间。

以 Linux 为例,权限系统设计的时候是沿袭自 mainframe/terminal 的多用户单主机模式,权限服务的是隔离设备的所有者(巨型机的所有者或者管理员,赋予 root 权限)和用户(终端登录,普通权限)。所以凡是涉及到对于硬件的操作,都要高权限。

但是当今环境里,电脑几乎都是“个人”的,安全威胁模型也逐渐转变成为,隔离不同的应用程序,于是有了沙盒等配套机制。要让这套系统良好运作,需要的是所有应用程序主动按照权限机制重写。
233 天前
回复了 vx7298 创建的主题 Ubuntu ubuntu 一如既往的优秀!
@ityspace #47
@424778940 #65
@Jirajine #51

作为发行版来说,我认为现阶段 NixOS 是不够严肃的,定位大致可以接近于弱化版的 Arch 。作为语言的 Nix 和作为包管理的 Nixpkgs 也不能直接拿来对比的,但是这三个经常被混在一起。比如 NixOS 官网提到的三个特性关键词 Reproducible/Declarative/Reliable 实际上分别是描述 Build ( Nixpkgs )/Language ( Nix )/OS ( NixOS )的。(另外官方的正式名称 Nix 既是语言,又是包管理器,Nixpkgs 指的是软件仓库)

按时间来说,我印象是先有的 Nixpkgs 的想法,为此设计了 Nix 语言,最后才有的 NixOS 。早期(至少十年前了) Nixpkgs/Nix 的文档实在是太简陋了,大部分时间我都是看着代码去猜的……当然现在易用性好了很多,只是我个人的评价是,Nix 系的设计目标是有可借鉴之处的,但是实现方式和哲学我不是很认可。

当年我被 Nixpkgs 吸引的原因是它希望用新的方式(在当时是很超前的思路)*同时*解决包管理器常年存在的几个问题:一是多版本共存,二是依赖解析,在此基础上顺便提供两个新特性:支持原子操作和回滚且无需依赖特定文件系统,再就是 Reproducible 构建。实现方式上,用的是现在被很多包管理借鉴的叫做 Content-addressable storage 的存储后端,以及类似沙盒化的构建过程(主要是记录依赖树而非权限限制)。

换个简单的说法,这个包管理的思路就是:记录依赖树上每个包的版本,同名包不同的版本会各存一份,相同版本也只存一份,而目标应用的执行环境(沙盒)中只有用得到的特定版本的依赖。这是个很理想化的设计,也存在很现实的问题。根据 Debian 过去十多年的构建经验,符合 Reproducible 构建标准(二进制一致)的包的占比一直在 85% 左右(构建这个行为会引入包依赖之外的变量)。Nixpkgs 意义上的 Reproducible 更接近于容器镜像可移植 Portable 的概念,即每次构建可能不同,但可以相互替换。

对于开发者来说,一般不关心打包的事情,但多数都会提供构建脚本。对于打包者来说,针对特定系统的打包希望尽量标准化,配置尽可能简单,原作者的构建脚本能拿过来用最好。对于包管理器来说,依赖最好是清楚写明的而不要是推断的。在 Nixpkgs 的设计者来说,当时没有合适的工具,所以想要创建一门新的语言,功能需求至少有几个:有脚本的灵活,声明式的写法以及模板化的生成能力。

至少在 Nixpkgs 层面,我是很欣赏设计思路的。到了 Nix 这门语言的层面,我的能力不足以做出准确的论断。仅从用户的角度上说,学习成本和易用性门槛都是相对比较高的。我个人认为 Nixpkgs 可以和 Gentoo portage 相媲美,都可以作为非常好的构建平台。

当包管理器想要往发行版方向发展时,限制就显现出来了。我一直说 RedHat/Debian/Suse 系是相对严肃的发行版,主要是因为它们都有足够的维护者。Nix 目前官方仓库的维护水平差距还是比较大的,而且可以遇见由于用户基数和门槛的限制,这个改善过程会很艰难。

另一方面我认为 NixOS 在发行版或者操作系统的理解定位上有点走偏了,就好比 Nixpkgs 是很好的锤子,但是 NixOS 太想把发行版塑造成钉子的形状。而且无论是 Windows/Linux/macOS 经过这么多年的发展,比较明显的共识是操作系统应该追求 Immutable 而不是 Reproducible 。这方面 NixOS 的实现方式永远比不上基于文件系统( layered )的效果,所以 NixOS 只能宣传自己 Reliable 却无法说自己是 Immutable 。

再就是用户数据理应和系统分离,应用程序理应和配置分离。NixOS 推崇的把用户、配置纳入进来的思想在我看来耦合有点过深了。不是说这样做不好,只是不太符合我个人的 Linux 哲学认知。
234 天前
回复了 kkocdko 创建的主题 Linux Linux 笔电的所谓省电技巧
@Donduck #52

这个本来是想放到第三层里说的。有能力通过 ec/smu/acpi 方式去调节的话,确实比系统自带的方式好。因为是硬件平台相关的,所以没有什么通用性。(我感觉 6000 系之后离电性能限制就很小了,不清楚是不是个例)

追根溯源的话,Intel/AMD 的市场竞争导致双方都在比拼频率,毕竟评测的时候会好看一些。原本默频应该是芯片工艺的甜点,睿频功耗高但是能换来一定提升。现在标定的默频都已经是睿频水平了,睿频实际上在 HWP 调度下变成了出厂灰烬超频。结果就是续航不理想,然后 AMD 那边 BIOS 还有平台配套开发不太给力,所以简单一刀切限制了离电性能。

对于普通用户来说,在没有这样的应用的情况下,想要调节就很困难了。多数都是通过逆向厂家给的控制程序,判断是 WMI 还是 ACPI 方式,又或者是 EC/SMU 控制,然后在 Linux 写程序还原这个操作。
234 天前
回复了 kkocdko 创建的主题 Linux Linux 笔电的所谓省电技巧
这个帖子竟然跑题了……我补充一点可能用得上的吧。

关于调度:

如果要详细说 CPU 调度,大约可以分三个部分:

- 系统层面最上层是一个“电源管理”机制,用途是让用户告诉操作系统,我的需求是性能全开,还是静音保证续航。多数实现就是性能/平衡/续航这样的选项。

- 第二层是 scaling driver ,这是系统内核向 CPU 发送调度指令的中介。用户层面的电源管理应用,将用户的意图转化为具体的调度参数。

在早期 Intel/AMD 都没有特别的调度机制的时候,这个调度参数其实是 ACPI 标准中的 P-State ,P 状态的定义就是多少电压对应多少运行频率。这个时代,调度还是软件占主导的。随着现代 CPU 越来越复杂,基于 ACPI P-state 调度不够用了。所以现在 intel_pstate/amd_pstate_epp 也变成了中介,不再直接控制频率,而是将意图给予 CPU 由 CPU 自身的硬件 HWP 来确定实际的调度。

- 第三层就是 CPU 和主板 BIOS 了。CPU 能否真正完成调度还要看 BIOS 限制,比如 PL1/PL2 这种长短功率限制、睿频开关等等。笔记本厂家通过 BIOS 完成所谓的“调教”。

部分开发能力较强的厂家,还会提供用户空间的控制程序,在 BIOS 之外完成相关参数设定。相当于手动覆盖第一层系统的电源管理机制,只是我没见哪家厂商给 Linux 做相关的适配,控制程序多数只能在 Windows 用。



综合上面所说的,对于用户来说,第二层是一定要用 Intel/AMD 自家驱动的。至于用户空间的第一层调度,我的建议是用系统自带的就好了,折腾不出什么花样来。我自己的话还是有能力利用一下第三层的,比如把 Windows 的控制程序移植到 Linux ,这个事可以有机会再说。


关于 TLP:

我的建议依旧是不要折腾,TLP 代码就在那里躺着,实现方式就是脚本。看过了之后再下判断也不迟。
235 天前
回复了 KinsleyNg 创建的主题 Linux 这个 Linux 的 chrome 硬解就这么难处理吗
根据 Pop!_OS 加上 nvtop 的提示,我估计是内核版本低了,驱动比较老,没有暴露足够的 sysfs 抽象。780M 是 RDNA3 架构,大概要 6.4 之后的版本才能有比较完整的驱动支持。Pop 是 Ubuntu 22.04 LTS 的衍生版,内核可能还是 5.X ,我不确定官方仓库是不是有足够新的内核。首先要保证内核版本够高,能正确加载 amdgpu 驱动。

如果你说的 chrome 是官方那个 deb 版本,硬件加速是通过 vaapi 实现的,这个版本编译的时候是带了 vaapi 支持和相应解码器的。(某些发行版仓库里 chromium 编译时未开启 vaapi ,像红帽系的发行版 fedora 由于版权问题是缺少一些解码器)

然后为了支持 vaapi ,需要 libva-mesa 驱动。libva 还有个使用 vdpau 后端的驱动,主要是供老一点的型号用的,vdpau 在新架构上基本被 libva-mesa 替代了。

再之后由于 mesa 在 amd 设备上只支持 Xorg/XWayland ,所以要保证 chrome 运行在非 wayland 模式下。如果你没手动指定的话,大概率 session 就是 Xorg 的。

最后就是 chrome 播放视频验证的时候要确认编码格式。说这么多其实我猜测比较可能的原因就是驱动版本和 libva-mesa 。安装好了应该就正常了,还不行你可以再来反馈或者去报 bug 。
续航这个事情,用户能做的事情不太多。不想折腾的话就是买有驱动支持的型号,然后装内核版本尽量高的发行版。


这个事情误区太多,我就多说一些。

首先功耗是动态的,如果拿手机来类比,一般看续航都是所谓的亮屏,然后才是待机。换句话说,高负载一般是用户控制不了的,能优化的其实是低负载的部分。其次如果考虑功耗构成的话,有负载的时候,显示屏可能在 3~5 瓦左右(根据分辨率和亮度会变化),SoC 等等根据平台可能有 15/30/45 瓦这样。低负载的情况下,显示屏还是 3~5 瓦,但是 SoC 可能会降到 1~2 瓦的水平。

所以很容易得到两个结论,工作负载基本只能看耗电大户 SoC 的工艺水平,也就是能耗比,越先进的平台一般越省电。待机功耗已经非常低了,即便把 CPU 降频降压等等,对于全天使用这种混合工况,能够改善的空间也很有限。


那现在硬件厂商、操作系统在不影响用户体验(性能释放)的前提下,还有哪些改善续航的手段?

第一个思路是改变高、低负载工况的比例。这是基于 CPU 的特性功耗范围非常大,短时间高性能快速完成计算任务然后待机,要比长时间中低性能的模式平均功耗更低。以前 CPU 多数运行在甜点频率,然后睿频能够起到平衡性能和功耗的作用,现在越来越出厂灰烬,所以想要长续航还是尽量选为移动平台设计的处理器版本。

第二个思路是没有低负载也要创造低负载,用不到的设备就让它待机。近几年的硬件几乎都支持运行时状态调节,大到 CPU 小到 WiFi 网卡 SSD 硬盘,都可以工作在高性能/节能/待机等模式下,整体都符合 ACPI 规范。如果所有硬件都支持,那么待机功耗能够降低到非常可观的水平。如果硬件平台比较老,或者某些设备只有 Windows 驱动,就会造成无法进入低功耗状态,因而导致续航功耗降不下来。

顺着第二个思路继续延伸,待机的意义其实可以放得更宽。想象一下浏览网页的场景,可能用户只会断续滚动一下以页面,其他时候都是在阅读。这个断续无操作的场景,就是可以激进待机的时机。于是在多方努力下,除了正常工作状态、待机和休眠,又多了一个叫 ModernStandby/s0ix/s2idle (分别是 Windows/Intel/Linux 的叫法)的状态,这个状态功耗接近待机,而唤醒时间非常短。

不过这个特性属于不能用短板那种,一旦某个设备无法进入 idle 状态很可能会把整个系统拉下水。之前 Intel 搞 EVO 认证其实就是这个用意,强制厂家筛选硬件保证这个激进待机能正常运作。

显示屏是这个机制非常好的受益者。高分辨率高刷新率显示屏其实是耗电大户,因为它同时会使 CPU 显示屏工作在有负载状态,还使得二者之间的链路( pcie/hdmi )都更加耗电。上述的待机场景,显示内容是不变的,如果显示屏能缓存输出信号自行显示,即可让 CPU 显示屏和链路都进入低功耗状态,这个技术叫 PSR 面板自刷新。配合 s2idle 机制,在长时间使用时可以将平均功耗从 3~5 瓦的水平降低一半。


之所以在上面说软件能做的事情不多,是因为新平台、新技术和新内核的加持下,默认就很好了。我有一台 Intel 11 代的 16 英寸笔记本,型号就不提了,电池容量大概 70Wh 。用 Linux 没做任何设置,满足 8 小时写代码开发工况是很容易的。满电待机(不是 s3 而是 s2idle )能有 250 小时,也就是说不含显示的功耗能低到 0.3W 左右。

如果真的要折腾一下,Nvidia 独显用户可以考虑 Bumblebee 做个切换。驱动尽量新一点,这样内核可以在 idle 状态下同时让显卡也降低功耗。CPU 调度方面 intel_pstate/amd-pstate 就很好,比绝大多数主动调度要靠谱,可以根据情况手动切换是否开启睿频。至于 TLP 在较新的硬件平台上已经没有什么作用了,针对老硬件不支持运行时电源状态管理的,最好的办法是 udev 规则手动加载卸载。缺乏驱动支持的设备,比如指纹识别什么的也是同理。重点就是关注有没有什么硬件或者 usb 设备影响了系统进入待机状态。

最后说一下 UPower ,它几乎运行在所有发行版上。原本的用途并不是省电,只是恰好有控制硬件中断和延迟的功能。这样即使用户空间的应用程序有不正常的硬件 polling 行为,也不会影响到硬件层面的 idle 和唤醒。(安卓在这个思路上继续延伸,wakelock 增加了对齐机制。)
TLDR: Hyprland 总体来说很不错,日常可用稳定性也可以,开箱即用的水平中游偏上,比不上 KDE/Gnome ,但好于其他同类 wm 。

目前 Wayland compositor 的实现主要有三个:Mutter(Gnome)/Kwin(KDE)/wlroots(sway),前两个与 DE 绑定,其他桌面实现包括 Hyprland 都是基于 wlroots 的。区别重点有两方面,一是对于协议的支持,协议指的是各种功能规范,比如输入法、锁屏等等,另一方面是渲染后端支持。

渲染后端指的是 framebuffer 的抽象,Intel/AMD 显卡用的是 GBM ,而 Nvidia 用的是 EGLStreams 。Kwin 已经官宣放弃支持 EGLStreams ,Mutter 支持比较好,而 wlroots 从一开始就不支持(有 fork )。Nvidia 的闭源驱动从 495.44 之后提供了 GBM 的实现,所以新一点的显卡,是可以正常跑 Wayland 的,但是有不少兼容性问题,或者用开源驱动,但就是功能和性能不太行。

窗口合成器一般分 stacking/tiling/scrolling 几类,帖子主题是 Hyprland 那就只聊 tiling 类型的。现在基于 wlroots 的主流实现就是 Sway/Hyprland/dwm/river 几个。基于 wlroots 也有可能是基于某个版本的 fork 实现,所以支持的渲染后端和协议也不同,比如 Hyprland 就早于 Sway 支持 input_method_v2 ,但由于 Hyprland 通过 GLES 做的特效,所以尽管上游 wlroots 已经支持 vulkan 但 Hyprland 就无法支持。

dwm/river 我没怎么用过,就不评价了。如果要在 Hyprland/Sway 里面选,除了上面说的 vulkan 支持,就看你的使用习惯是手动还是自动布局。Sway 是纯手动的,Hyprland 支持 MasterStack 模式的自动布局。Sway 开放了 IPC 实际是可以很简单实现自动布局的,不仅可以用 Python 之类的脚本来写,也有其他语言的 binding 。Sway 自由度更大,加上是 wlroots 的官方开发,所以稳定性好很多。



PS 随便多说点,给想尝试 Sway/Hyprland 的做个参考。

由于 Sway/Hyprland 只是个 WM ,所以真要日常使用需要额外配置很多东西,这里列举几个影响使用体验的因素。

第一个就是常说的分数缩放,然后又要分 Wayland 原生应用和 XWayland 应用。(后者只有 KDE 做了额外的支持,其他几家不做的原因主要是理念,wlroots 这边的意思是,不该我管的事情我不管。) XWayland 应用一定会模糊,但是这个影响已经很小了,现在 Chrome 系和 Electron 都已经有了原生实现,可以通过命令行参数切换到原生模式。

相关的协议 wp-fractional-scale-v1 大概是去年合并的,在此之前的渲染逻辑是 200% 渲染之后 downsample 。新协议变成了客户端(应用程序)可以获知缩放参数,然后自己完成界面逻辑。只是这个新协议要 GTK4/QT6 的支持,不过在不支持新协议的应用上( GTK3/QT5 )还是会回到之前的模式上。无论哪种模式都不会模糊,只是新协议不需要 200% 渲染这种低效的操作了。

第二个多屏幕支持,我个人认为非常好了,不同显示设备可以支持不同 DPI 。由于 tiling 模式是不存在某个状态一个窗口横跨两个显示设备,所以处理起来简单很多。

这里 Sway 有个优于 Hyprland 的功能叫 multiseat ,可以按照 seat 为输入输出设备进行分组,每个 seat 有独立的输入焦点。这个功能对于多输入设备(触摸板/鼠标/触摸屏)和多屏非常有意义,比如多焦点的情况下,可以在副屏上触摸操作,而不影响主屏。

第三个是中文用户关心的输入法。由于 wayland 输入法相关的协议众多,基本不建议用 fcitx5 之外的任何输入法框架,因为 fcitx5 已经实现了所有能支持的一切。Sway 1.9 也合并了 input_method_v2 ,所以和 Hyprland 没区别了。应用程序侧 input_method_v2 主要是影响某些终端 vte 显示候选框的问题,chrome 系支持了 wayland-ime 和 gtk4 也可正常使用了。

第四个是状态栏,主要的实现是 waybar/yambar/eww 几个。最大的麻烦是 tray 支持,即在状态栏显示客户端应用自定义的图标,并支持交互功能,毕竟不是 Gnome/KDE 不会有应用专门去做相关支持的。waybar 稍微好一丢丢,因为它本身是个 gtk 应用,eww 算是套壳,yambar 需要自己想办法。

小问题是状态栏的工作逻辑,我看到的除了 yambar 大部分功能实现都是基于 polling 做状态轮训,这其实是个比较低效的做法,比如显示笔记本当前电池电量,正确做法其实是通过 udev 规则触发显示更新。再比如不使用 tray 的情况下,要显示输入法状态轮训就不合适,因为这个状态切换需要接近实时,然后绝大部分时间状态是不变的。合理做法是用 fcitx5 lua 功能监听状态变化事件,然后主动触发显示更新。

第五个是锁屏。之所以说这个是因为锁屏可选的实现很少,目前一定要正确实现 ext-session-lock-v1 才可以。原本 Linux 图形界面的通常逻辑是在 tty 之上的,比如 Gnome 登录界面( gdm )一般在 tty1 上,登录验证完成后,在 tty2 上开一个当前 session 的界面,锁定后又会回到 tty1 上。目前应该是只有 Gnome 把锁屏做到了 wm 上,其他都是用一个单独的程序当作锁屏。这就是前面那个协议的意义,这个锁屏程序如果 crash 掉了,正确实现协议可以保证不会意外解锁。

第六个是截图录屏,放到前两年这还算个事,调用 wayland 实现的截图录屏软件就都没什么问题。有问题的是类似 XX 会议这种,如果自身是 XWayland 实现,就没法共享桌面,有极其复杂的曲线救国的方式但是不推荐。如果是基于浏览器或者 electron 的实现,就可以正常录屏,因为相关调用走的是 wayland 协议。

剩下的放到一起说,主要是 systemd 集成和各种权限问题。我估计应该没有人刻意不在桌面环境用 systemd ,大部分人都会主流 DE 选一个然后再另外安装 Sway/Hyprland ,少部分 Arch/Gentoo 用户会直接上。systemd 能简化很多事情,但不熟悉的话很多细节问题就难以解决。

举个例子来说,因为不是 DE 而是 WM ,就连调节屏幕亮度这样的功能也要自己写脚本实现。但是调节亮度需要操作对应设备 sysfs 文件,那就有几个思路:要么给当前用户 sudo 命令免密码的授权,要么 udev 规则给当前用户显示设备权限,要么写个程序赋予 SUID ,或者是调用 systemd-logind DBus 接口。从安全的角度来看,当然是最后一个方法最合理,但是绝大多数人应该都不了解。

再比如 systemd-logind 默认对于电源按键的响应是关机,希望调整为待机,如果是 Gnome 这种,可以在系统设置里去调整。按照一般思路,用户会去修改 logind 的配置。但按照 systemd 的设计和各个主流 DE 的实现,符合逻辑的做法是通过 systemd-inhibit 获取 handle-power-key 的锁,这样 systemd 会忽略对 XF86PowerOff 按键的响应,之后 WM 可以自行绑定相关按键来执行想要的功能。

如果是从其他 DE 迁移过来,之前依赖特定桌面的功能,比如 keyring 什么的,就需要调用对应的 polkit agent 来获取授权。其实 DE 做的有关用户体验的事情非常非常多,想要全都迁移到 WM 层面就需要非常多的工作。我再举个例子,Gnome/gdm 的登录是可以支持同时对密码和指纹进行验证的,这里同时的意思是按输入键就验证密码,直接刷指纹也可以。我原本以为这是个很简单的配置问题,研究之后才发现,密码和指纹校验本身确实都是 PAM 机制来做,但仅仅依靠 PAM 机制是做不到同时检测的。PAM 的设计就是线性序列化的,要么先指纹,失败了再验证密码,要么反过来。想要做类似的功能就要写一个并行验证的逻辑,然后这个模块又必须正确实现锁屏协议,还要负责处理 session 相关的管理,这就不是个人用户能手搓的了。

做个简单总结,如果你对系统足够了解,开发能力足够强,Sway/Hyprland 这种 WM 经过自定义能获得比 Gnome/KDE 更好的体验,只是需要花费非常大的精力。
247 天前
回复了 frostming 创建的主题 Python 有一个包管理器叫 PDM,已经四年多了
PDM 真的很棒,我上次看到作者的回复,想赞美一下 PDM 结果打成 PNPM 了……

对于非专业人士和初学者,我还是会推荐 PDM 的。原因一是基于 Python 生态及现有标准,又比 pip 方便友好,二是成熟项目基本不会踩坑了。


从趋势来看,随着 AI 的兴起,这些年“包管理”这个概念慢慢在向“工程管理”方向转变。生态层面上,就看 uv 能不能统一 Python 工具链了,在这个背景下,独立的包管理工具可能会慢慢淡出视线。

另外 conda 的思路也不一样,我个人认为它更接近于 docker build 这种重建。

就同楼主说的一样,专业人员肯定是要考虑实际需求做选择的。
249 天前
回复了 zachary99 创建的主题 Linux 哪个版本 Linux 对多屏显示友好
第一个大概率不是软件问题。建议详细描述一下笔记本型号、硬件配置和连接显示器的方式等等方便大家给出建议。

我印象中 Intel 过渡到真三显支持应该是 11 代之后的事情。在这之前对于三显的支持是内部一个,外部两个共用一个信号源,就是你所说的外接的两个显示器只能复制模式。Nvidia/Amd 独显也有类似的问题,但是原因不一样,主要取决于拓扑结构。

第二个问题,常见的登录界面( gdm/sddm )的软件逻辑是一个特殊账号的桌面,这个账号有自己的显示器配置。一般你把自己账号里面的显示器配置文件复制到这个特殊账号里面覆盖掉就好了。
251 天前
回复了 rockyliang 创建的主题 Go 编程语言 关于 golang 官网一段代码的疑惑
@tuoov #25

楼上提到的 https://research.swtch.com/mm 就可以。

另外我印象里内存模型这个概念出现得比较晚,Java 应该是第一个把内存模型理论化的,在这之前的比如 C 都是先有语言后建模,之后的新语言比如 Golang 才是先设计内存模型,后规范语言。

Java 后来发现原本设计的内存模型有问题,所以有了 JSR 133 来做修正。可以通过 http://www.cs.umd.edu/users/pugh/java/memoryModel/jsr-133-faq.html 向外发散阅读。
1  2  3  4  5  6  7  8  9  10 ... 13  
关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   实用小工具   ·   3142 人在线   最高记录 6679   ·     Select Language
创意工作者们的社区
World is powered by solitude
VERSION: 3.9.8.5 · 28ms · UTC 13:31 · PVG 21:31 · LAX 05:31 · JFK 08:31
Developed with CodeLauncher
♥ Do have faith in what you're doing.