V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
V2EX  ›  lesismal  ›  全部回复第 32 页 / 共 60 页
回复总数  1200
1 ... 28  29  30  31  32  33  34  35  36  37 ... 60  
2023-02-15 22:32:56 +08:00
回复了 aapeli 创建的主题 Go 编程语言 有一个 Golang 泛型的问题咨询
@Nazz
v 站回复的 markdown fmt 感人,看这里吧:
https://gist.github.com/lesismal/0cee2bb8d76c7f907a8e2a8401b4fd41
2023-02-15 22:27:44 +08:00
回复了 aapeli 创建的主题 Go 编程语言 有一个 Golang 泛型的问题咨询
@Nazz
看了下简单的基础类型泛型汇编

```golang
// main.go
package main

func SumInt8(a, b int8) int8 {
return a + b
}

func SumInt32(a, b int32) int32 {
return a + b
}

func SumFloat32(a, b float32) float32 {
return a + b
}

func SumFloat64(a, b float64) float64 {
return a + b
}

func SumGenerics[T int8 | int32 | float32 | float64](a, b T) T {
return a + b
}

func main() {
SumInt8(1, 2)
SumInt32(1, 2)
SumFloat32(1.0, 2.0)
SumFloat64(1.0, 2.0)
SumGenerics[int8](1, 2)
SumGenerics[int32](1, 2)
SumGenerics[float32](1.0, 2.0)
SumGenerics(1.0, 2.0)
}
```


```sh
ubuntu@ubuntu:~/generics$ go tool compile -S main.go
ubuntu@ubuntu:~/generics$ go tool objdump main.o
```

```asm
TEXT "".SumInt8(SB) gofile../home/ubuntu/generics/main.go
main.go:4 0x2cb7 01d8 ADDL BX, AX
main.go:4 0x2cb9 c3 RET

TEXT "".SumInt32(SB) gofile../home/ubuntu/generics/main.go
main.go:8 0x2cba 01d8 ADDL BX, AX
main.go:8 0x2cbc c3 RET

TEXT "".SumFloat32(SB) gofile../home/ubuntu/generics/main.go
main.go:12 0x2cbd f30f58c1 ADDSS X1, X0
main.go:12 0x2cc1 c3 RET

TEXT "".SumFloat64(SB) gofile../home/ubuntu/generics/main.go
main.go:16 0x2cc2 f20f58c1 ADDSD X1, X0
main.go:16 0x2cc6 c3 RET

TEXT "".main(SB) gofile../home/ubuntu/generics/main.go
main.go:32 0x2cc7 c3 RET

TEXT "".SumGenerics[go.shape.int8_0](SB) gofile../home/ubuntu/generics/main.go
main.go:20 0x2ed6 8d040b LEAL 0(BX)(CX*1), AX
main.go:20 0x2ed9 c3 RET

TEXT "".SumGenerics[go.shape.int32_0](SB) gofile../home/ubuntu/generics/main.go
main.go:20 0x2eda 8d040b LEAL 0(BX)(CX*1), AX
main.go:20 0x2edd c3 RET

TEXT "".SumGenerics[go.shape.float32_0](SB) gofile../home/ubuntu/generics/main.go
main.go:20 0x2ede f30f58c1 ADDSS X1, X0
main.go:20 0x2ee2 c3 RET

TEXT "".SumGenerics[go.shape.float64_0](SB) gofile../home/ubuntu/generics/main.go
main.go:20 0x2ee3 f20f58c1 ADDSD X1, X0
main.go:20 0x2ee7 c3 RET
```

int8/int32 的泛型生成的汇编是用的 LEAL 指令,非泛型的是 ADDL 指令,虽然都是一条指令但是 LEAL 内存加载应该比 ADDL 慢一点,这应该是你说的换成 int 会快一点的原因吧;
另外,泛型 float32/float64 生成的汇编与非泛型的汇编是一致的,都是 ADDSS/ADDSD 指令,所以性能应该没差别。
2023-02-15 18:59:20 +08:00
回复了 aapeli 创建的主题 Go 编程语言 有一个 Golang 泛型的问题咨询
@Nazz

> 你想表达的应该是 golang 泛型是有开销的, 而不是泛型可以提高性能.

基础类型和非基础类型的性能影响是不一样的

> 同样两份代码, 把参数换成泛型, 不可能会提高性能

你的测试没有提供代码,我没办法做测试结论对应的代码的因果分析


我前面的描述是有说明了场景条件的,但你可能还没仔细看所以没聊到一个点上,大概总结下
1. 主要限定于自定义 struct 类型接口调用,其他语言可能是 class 多态,所以请不要用基础类型来比较性能是否提升
2. 范型本身的实现方案,有办法提高性能的策略,但要看具体语言编译器的实现,go 的目前应该是没有提高,但 c++ rust 那些应该是提高了
3. 编译器优化能够做到范型静多态 /单态化相比于接口或面向对象 class 多态提高性能不是说 go 现在已经提高了,为什么能提高,请看下 #31

如果你熟悉一些 c++,可以简单看下《深度探索 c++对象模型》,对实际业务用处不大,但是对语言这块的理解会有些帮助。编译器理论过于深入我也不懂,其他讲编程语言的书鲜有涉及这块,所以推荐这本书
2023-02-15 16:05:56 +08:00
回复了 aapeli 创建的主题 Go 编程语言 有一个 Golang 泛型的问题咨询
@GeruzoniAnsasu
对,go 是解放了设计阶段的一些束缚,避免了面向对象的鸭嘴兽难题,避免了面对新领域尚无成型的领域设计加之快速迭代需求时顶层设计尾大不掉重构成本过高的问题。
如果项目非常复杂,新人接手之类的,没有自顶向下的类型关系图,这种自下而上地去熟悉代码也是有点心累,要靠个人阅读搜索分析代码和业务理解能力了。但相比于面向对象,这点成本其实要轻松得多。
2023-02-15 15:54:02 +08:00
回复了 aapeli 创建的主题 Go 编程语言 有一个 Golang 泛型的问题咨询
@Nazz #29
> 泛型本来就不能提高性能啊, 只是减少重复工作. 一个 Max 肯定比 MaxInt64, MaxInt32...优雅

你这里所述的是基础类型,我 #17 所说的 "泛型适合运算符重载之类的基础类型场景" 就是支持这样做的,这种场景我对使用泛型没有异议。

"泛型本来就不能提高性能啊" 是不正确的,只是 go 目前的泛型实现方案对自定义结构体类型没带来性能提升,但是其他一些语言是做到了的。我觉得可能是由于 go 自带 runtime 面临着更多复杂性,所以目前阶段没有支持这种优化,或许随着 go 未来版本编译器的优化也能带来这些提升。

泛型"能够"性能提升的场景主要是自定义的结构体类型相关的。
你可以先找些资料来研究下,比如面向对象多态的函数表,比如 c++模板静多态,我前面提到的 "泛型会让你的 Go 代码运行变慢",帖子里有讲比我更专业更深入的,引用一段落:
"从历史上看,C++、D 乃至 Rust 等系统语言一直采用单态化方法实现泛型。造成这一现实的原因很多,但总体来说就是想用更长的编译时间来换取结果代码的性能提升,并且只要我们能提前把泛型代码中的类型占位符替换成最终类型、再进行编译,就可以极大优化编译流程的性能表现。装箱方法就做不到这一点。另外,我们还可以对函数调用进行去虚拟化以回避 vtable ,甚至使用内联代码实现进一步优化。"

甚至不需要去研究这些,你可以站在编译器和运行时的角度自行想象一下比如:interfaceA 的一个实例在进行方法调用时,如何通过这个实例的指针实现方法调用?
首先你得知道这个实例的类型,否则A类型调用到B类型的方法上去就乱套了,go 的指针是胖指针,指向的内容一部分是类型元信息,一部分是指向值本身。要实现实例化调用,首先要通过这个 interfaceA 实例的指针去解引用取得类型信息、找到函数表,然后再把值和函数结合起来使用。这是由 runtime 来处理的,运行时才能搞定。
而静多态 /单态化,比如 c++模板,是编译期就生成了对应类型的代码,与上面说的 interfaceA 由运行时处理对比一下,静多态 /单态化可以把 runtime 先确定类型信息这一步省掉,虽然只是一些简单的指针操作,但量大积累起来,性能差距就拉开了。
2023-02-15 13:28:02 +08:00
回复了 aapeli 创建的主题 Go 编程语言 有一个 Golang 泛型的问题咨询
@lesismal #27 更正
我觉得这样说不太正确。接口与面向对象是不同的:
1. 面向对象是自顶向下的约束,非父类本身或者子孙类无法作为祖辈参数的实例入参传入;
2. 接口没有强制的向下约束,而是以更自由松散的方式、按需定以按劳分配,完全可以根据需求、抽取需要实现的方法集合作为一个单独的接口定以去作为其他公共形式的使用。可能是由于大家平时一些自定义类型需要去实现标准库已经定义了的接口,才会有这种被约束的错觉。
2023-02-15 13:26:10 +08:00
回复了 aapeli 创建的主题 Go 编程语言 有一个 Golang 泛型的问题咨询
@GeruzoniAnsasu #25
> 接口有个很大的问题是不能只实现部分

我觉得这样说不太正确。接口与面向对象是不同的:
1. 面向对象是自顶向下的约束,非父类本身或者子孙类无法作为祖辈参数的实力传入;
2. 接口没有向下约束,而是以更自由松散的方式、按需定以按劳分配,完全可以根据需求、抽取需要实现的方法集合作为一个单独的接口定以去作为其他公共形式的使用。可能是由于大家平时一些自定义类型需要去实现标准库已经定义了的接口,才会有这种被约束的错觉。

比如 struct A 实现了 M() X() Y(), struct B 实现了 N() X() Y(),泛型需求是需要在泛型方法里调用 X() Y()。可以定义一个 interface XY{ X() Y() } 抽取他们公共的部分,以接口的方式替代泛型,当然,这个单独的定以是一点额外的成本,但并不算复杂。

标准库的很多接口定义,是根据多年的工程实践总结出来的一些通用抽象,这些是属于基础设施、系统编程范畴的。
而相比于标准库、我们基于其上构建的,基础库也好、业务需求也好,更多的是基于自己需要来进行定义,这些自定义的约束来自自己的设计、可以灵活处理,并不需要太被标准库约束。
2023-02-15 13:10:15 +08:00
回复了 aapeli 创建的主题 Go 编程语言 有一个 Golang 泛型的问题咨询
@Nazz

> 泛型声明里面限定了 int, float 之后自然可以做加减乘除, 更复杂的操作即使有重载操作符也不够用, 我认为泛型主要是给写库的人使用的

泛型也好,接口也好,或者其他语言的面向对象多态,以及 c++的模板静多态(编译期为每种实际类型生成代码性能更好),所有这些都是为了系统设计时的公共操作抽象做语法基础。
所以不管是泛型还是接口,都是需要这些“同类”obj 具有一组相同的操作(运算符或者方法)。

就 go 而言,除去基础类型能支持运算符,对于自定义类型的 struct ,只能通过方法来实现相同操作,而对于多个类型的同名同形式方法,以接口的方式作为其他方法的参数要比以泛型的方式来定义其他方法更简洁明了,而且版本兼容性也更好。

另外就是性能,截至目前,自定义类型的泛型性能应该是不够好的,可以搜一下 “泛型会让你的 Go 代码运行变慢” 这个帖子看看,或者其他关于范型性能的帖子。
所以对于写库的人来说,如果出于性能考虑,目前的泛型也是应该放弃,连接口都不要用而是把接口访问自己元类型这层中转的指针操作也省去、自己手撸不同类型的实现才是最好的选择,而对于写库的作者,其实用不用范型节省不了太多工作量,毕竟基础设施的领域不像 CURD 那样经常搞很多需求。
当然,如果后续版本的编译器能像 c++模板那样以静多态的方式来提高性能是最好了,然后如果性能敏感的场景来替代接口的方式,也可以不用手撸多种类型的实现替代泛型了

> 不熟悉模板元编程,但模仿 cpp 的话无疑会让 go 变复杂许多

并不是模仿 c++,而是目前 go 的泛型除去基础类型范畴,既没有性能提升也没有使代码更加优雅简洁。反倒是很多人为了泛型而泛型、本来不需要泛型的地方都想用泛型,反倒把 go 搞复杂了。
2023-02-15 11:26:54 +08:00
回复了 aapeli 创建的主题 Go 编程语言 有一个 Golang 泛型的问题咨询
@Nazz #18
对比下 c++,因为 c++有运算符重载,所以模板这种泛型里类也可以直接加减乘除这些运算符、因此不需要使用方法如 a.Sum(b)。而 go 里没有运算符重载,很多算法数据结构里即使使用泛型,仍然需要以接口的形式由 struct 实现接口,然后用 a.Sum(b)之类的函数调用方式来处理。显而易见,既然这种地方已经使用接口方法了,这里的泛型就是多余的了。
多数时候是在使用基础类型为主的地方,使用泛型可以节约代码、不用为每种类型都去实现一个 /一组方法或者单个方法内再做类型转换。而本来接口、切面就能做的地方,泛型是画蛇添足。

所以有了我在#17 的观点。

自从支持了泛型以后,不少人为了用泛型而用泛型,让 go 代码变得更丑陋了,很不希望看到一些大厂“架构师”又来污染放毒,因为太多小白会跟风,最终劣币驱逐良币。
2023-02-14 22:57:00 +08:00
回复了 aapeli 创建的主题 Go 编程语言 有一个 Golang 泛型的问题咨询
@Nazz
如果入参不是对应具体 interface{...}类型也是编译期就失败的。

@aapeli
个人感受是:go 的泛型适合运算符重载之类的基础类型场景,自定义类型通常用 interface{...}或者切面更加简洁清晰、硬要为了用泛型而去用泛型,可能会让代码更难看。

再根据我浅薄的 go 泛型使用经验(不超过 10 分钟),能用接口、切面解决的问题不要用泛型就是最优解。
——不一定对,仅供各位参考
怀念那些用牛皮纸包书皮的青春岁月
2023-02-07 15:53:14 +08:00
回复了 Nazz 创建的主题 程序员 求教,个人开源项目如何才能快速积累 star
@Nazz 来试试我这个,也支持 http ,github.com/lesismal/arpc
2023-02-07 14:52:42 +08:00
回复了 Nazz 创建的主题 程序员 求教,个人开源项目如何才能快速积累 star
@bitkuang8 一看当前 599 ,连项目是做啥都没看我就点了,图个 600 的喜庆
你们有没有也发现,OP 的头像跟 OP 本人确实挺搭的
2023-02-01 20:35:14 +08:00
回复了 DigitalHarace 创建的主题 程序员 不会说话的人如何学习酒桌敬酒文化
00 后整治职场,这里只建议 00 后不要学,否则就是四九国军。

PS ,标题第一眼看成了 “不会说人话的。。。”
让我们这些 7080 后的人情何以堪
好嘛,35 岁还不够早,感觉以后大厂要搞 32 岁毕业了
2023-01-19 14:32:57 +08:00
回复了 never2023 创建的主题 随想 父母也是势力的,看房 车 工资高不高,不看工种
@qeqv 这几年 AI 写作发展太快了,以后类似的会越來越多
2023-01-08 01:05:26 +08:00
回复了 never2023 创建的主题 随想 父母也是势力的,看房 车 工资高不高,不看工种
OP 的行文,不像是人,猜测是 AI 在进行机器学习吧。
2023-01-04 16:54:40 +08:00
回复了 fxjson 创建的主题 Go 编程语言 go 每一个数据库库使用起来都不太方便,有木有
其他那些功能太多了,涉及到的细节处理也多。
我的库主要就是在 std 基础上封装了反射的部分,rawsql 能用的部分依旧能用,封装过的 sqlw.DB 提供了一点便捷的方法,让用户减轻结构体与传参、结果绑定部分的工作量,如果熟悉标准库的 rawsql ,不需要去学习其他那些库的基础只是、看下例子就懂了
话说,我就是因为其他库太复杂了,学不懂 /动,所以一直使用 rawsql ,后来写了这个,省力多了
1 ... 28  29  30  31  32  33  34  35  36  37 ... 60  
关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   实用小工具   ·   1739 人在线   最高记录 6679   ·     Select Language
创意工作者们的社区
World is powered by solitude
VERSION: 3.9.8.5 · 25ms · UTC 16:41 · PVG 00:41 · LAX 08:41 · JFK 11:41
Developed with CodeLauncher
♥ Do have faith in what you're doing.