V2EX = way to explore
V2EX 是一个关于分享和探索的地方
Sign Up Now
For Existing Member  Sign In
V2EX  ›  grzhan  ›  全部回复第 4 页 / 共 23 页
回复总数  450
1  2  3  4  5  6  7  8  9  10 ... 23  
2024 年 11 月 28 日
回复了 hez2010 创建的主题 程序员 运行 100 万个异步并发任务需要多少内存
@lesismal 所以一般定时要求不严格的话很多 Golang 开源项目会给定时 duration 加个 10% 左右的随机抖动吧
例如 VictoriaMetrics 的 timeutil.AddJitterToDuration - https://github.com/VictoriaMetrics/VictoriaMetrics/blob/master/lib/timeutil/timeutil.go
2024 年 11 月 28 日
回复了 hez2010 创建的主题 程序员 运行 100 万个异步并发任务需要多少内存
我觉得不一定是拉踩,针对 Golang 的这一情况可以添加更多的说明会好一些。
2024 年 11 月 28 日
回复了 hez2010 创建的主题 程序员 运行 100 万个异步并发任务需要多少内存
如 @lesismal 老板所说,这里 golang 的开销应该是有栈协程的开销,一个 goroutine 栈默认最小是 2K ,1000,000 * 2K / 1024 / 1024 = 1.9G ,算上 sudog 等其他的结构加起来可能差不多吧。

实际 golang 处理异步任务的时候确实不会开这么多协程,更多是把协程池化然后基于 channel 通信来处理。

在实际 Golang 开发过程中确实会有每个 conn 对应一个 goroutine 大量连接导致内存过高的 case ,然后社区里确实有对应魔改 netpoll 以及 nbio 这样的方案来解决类似的问题。
2024 年 11 月 27 日
回复了 murmur 创建的主题 程序员 有多少兄弟被国产化改造坑过
@duebasser 主要我觉得作为闭源商业方案其性能与稳定性问题不想着如何解决,反倒变成了绑架与讹诈客户的工具,还是挺逆天的。
2024 年 11 月 26 日
回复了 murmur 创建的主题 程序员 有多少兄弟被国产化改造坑过
之前达梦的 .Net 驱动有很严重的问题,驱动执行一些 Insert SQL 失败会吞掉偶发异常,导致上层代码对于数据丢失不可知。

甲方对于达梦数据库的性能以及稳定性意见非常大,PG 32 核 64 G 的配置可以跑很稳妥的数据量到达梦这里好几倍的配置也经常崩溃以及丢数据,达梦那边给的回复是需要上集群版,张嘴报价几百 w…反过来绑架客户了

后面要求研发侧对于项目上国产数据库方案首推用人大金仓(好歹基于 PG )了,如果有甲方要求用达梦会郑重劝告( x
2024 年 11 月 23 日
回复了 aziya 创建的主题 分享创造 做了一个只有中国人才能玩的游戏
完成度很高👍
@qiuhang 其实我也猜测是这样,团队里没有专业一些的工程师。
但类似的专业度不足的问题过去几年已经出现过好几次了( helang 、掰天线……),公司应该是盈利的,他的人脉哪怕只是找个顾问把个关应该也不难(不找其他 up 主,找个大厂上班的老同学或者学长之类也成吧),到现在还没解决这个问题只能说 emmm……
主要还是营销技术宅人设带来的反噬。

何的早期视频看出来属于是一个有自己创意的数码爱好者(果粉?)。但应该是 5G 视频之后为了接住这泼天的流量,何开始往“技术宅”人设开始转型,然而问题在于和手工耿、稚晖君不同,他自身的专业水平是不足以支撑这个人设的。

如果何同学团队具有一般软件工程师的专业知识储备,就会知道:
1. ascii generator 本身很简单,就算是自己写的也没啥了不起的,没必要抄别人的项目
2. 如果基于别人的开源项目实现也 OK ,但要遵循别人项目的开源协议,哪怕是非常宽松的 MIT
3. 哪怕再恶劣些,我抄了别人的项目说是我做的,但只要我视频里没有证据也不会有什么舆情,因为还是那句话 ascii generator 很简单,自己会写也不奇怪。

最烂的情况就是现在:我拿了别人项目声明是自己写的,然后把证据放到视频里还不自知。

之前看过何同学关于自己在互联网舆情的访谈,我觉得多少是可以理解的,但我也确实没想到 2024 年快结束了他们团队还会犯这种基础的专业性错误,确实有点自业自得了。
2024 年 11 月 15 日
回复了 pike0002 创建的主题 Go 编程语言 Go 语言中的接口 nil 检查需谨慎
主要就是 @CLMan 老板提到的接口类型约定的是方法调用,所以大部分场景不需要检查接口的值是否为 nil ,要检查通过类型断言或者方法内部来检查。

Go 的语法细节确实有很多实际运行起来不符合直觉的地方,很多时候一定要结合它的内部机制甚至看源码才能顺畅理解, 虽然这些机制大部分理解了也确实比较简单吧(所谓“大道至简”…
2024 年 11 月 14 日
回复了 IIInsomnia 创建的主题 Go 编程语言 从 0 到 1 手撸一个协程池
感觉控制协程数量的场景属于内部造轮子可以解决的轻量需求,如果是我的话大概率也是自己造轮子来定制(参考开源实现)而不是引入第三方依赖。
发现自己贴错了 issue: https://github.com/golang/go/issues/64825 ( x
这个讨论还提到一个点,就是常量,现在常量由于这个类型转换的限制可能对于同个常量会写两个类型( bool/int ),这在 Go 编译器和运行时的代码里就有出现( src/internal/goexperiment/exp_arenas_on.go ):

const Arenas = true
const ArenasInt = 1

总之这事目前来看没有明确拒绝的理由,更接近于懒得搞,如果有人愿意费力气把这变更做了,感觉 Go 团队这边也会接受。

(其实这种类似的情况在 Go 社区有很多,习惯就好)
我翻了下 Go 的 Github Issue ,其实有非常多人的提过这个 int(bool) proposal 。
包括今年也有关于这个的讨论: https://github.com/golang/go/issues/6011

看得出来不少 Go 团队的成员是支持加入这个特性的,主要反对的人是 rsc ,他认为这个变更工作量很大,同时觉得收益不高,Go 团队时间有限,要搞的话你们自己搞:1. spec 的变更; 2. 编译器和 go/types 的变更; 3. 把 Go 主仓库相关代码更新了以尽可能用上这个新特性,来证明这个特性对于 Go 而言是有用的。

所以感觉更接近 Go 早期版本忽视了这个特性,然后现在随着 Go 发展要加进这个特性工作量大了就懒得搞了。
2024 年 11 月 12 日
回复了 winglechen 创建的主题 程序员 写本技术书,能不能熬过这个内卷时代。
draveness 大佬的《 Go 语言设计与实现》我之前一直看: https://draveness.me/golang/
他应该就是先写系列文章( gitbook ),在收获比较高的访问量与关注度之后,出版了实体书。
我觉得是个路子,然后像 draveness 会画很多配图(确定好配色后用 sketch 画),对于这种源码分析读物的可读性提升了很多,但也更费功夫。
供参考。
2024 年 11 月 10 日
回复了 xoxo419 创建的主题 程序员 你见过哪些好玩又花里胡哨的代码呢
Golang 标准库 sys.TrailingZeros64
通过 deBruijn 序列快速计算一个 64 位二进制末尾有多少个 0:

var deBruijn64tab = [64]byte{
0, 1, 56, 2, 57, 49, 28, 3, 61, 58, 42, 50, 38, 29, 17, 4,
62, 47, 59, 36, 45, 43, 51, 22, 53, 39, 33, 30, 24, 18, 12, 5,
63, 55, 48, 27, 60, 41, 37, 16, 46, 35, 44, 21, 52, 32, 23, 11,
54, 26, 40, 15, 34, 20, 31, 10, 25, 14, 19, 9, 13, 8, 7, 6,
}

const deBruijn64 = 0x03f79d71b4ca8b09

// TrailingZeros64 returns the number of trailing zero bits in x; the result is 64 for x == 0.
func TrailingZeros64(x uint64) int {
if x == 0 {
return 64
}
// If popcount is fast, replace code below with return popcount(^x & (x - 1)).
//
// x & -x leaves only the right-most bit set in the word. Let k be the
// index of that bit. Since only a single bit is set, the value is two
// to the power of k. Multiplying by a power of two is equivalent to
// left shifting, in this case by k bits. The de Bruijn (64 bit) constant
// is such that all six bit, consecutive substrings are distinct.
// Therefore, if we have a left shifted version of this constant we can
// find by how many bits it was shifted by looking at which six bit
// substring ended up at the top of the word.
// (Knuth, volume 4, section 7.3.1)
return int(deBruijn64tab[(x&-x)*deBruijn64>>(64-6)])
}


-------

1. (x & -x) 会把 x 除了最低位以外的 1 都清 0
2. deBrujin64 是一个德布鲁因数,它有个重要性质:它相当于一个循环的德布鲁因序列,每个长度为 6 的二进制子串在里面恰好出现一次,也就是说这个德布鲁因数包含所有的 6 位二进制数,且通过不同次数的位移可以恰好得到。
3. x 若有 k 个 0 , 那么 (x & -x) * deBrujin64 就相当于 deBrujin64 << k ,注意左移是会回绕的,因此 (deBrujin64 << k) >> 58 可以恰好得到 k 在 deBrujin64 中对应的六位唯一二进制子序列
4. deBrujin64tab 就是 k 与对应六位二进制子序列(数)的映射,因此可以快速找到对应的 k ,也就是末尾多少个 0

据说该方法来自 Knuth 的 The Art of Computer Programming 7.3.1 ,回头找一下再学习下。

sys.TrailingZeros64 的性能在很多 Go 运行时场景都至关重要,Golang 有很多 free-list allocator ,比如 mspan 就会用 allocCache bitmap 位图来快速定位可以分配的 free object ,所以计算这个 bitmap 末尾有多少个 0 ,就能快速找到可以被重复利用分配的 free object
其实 Golang 的 Embedding (嵌入) 也很灵活,因为嵌入不光能够嵌入 struct ,还能嵌入 interface 。

经典的实现是标准库 context ,比如实现 context.WithCancel 的关键结构体 cancelCtx ,就是嵌入了接口 Context ,当 cancelCtx 初始化时,会把 parent 塞给 cancelCtx.Context ,关键在于由于 cancelCtx.Context 是个接口,所以你可以把任意实现了 Context 接口的类型作为 parent 塞给 cancelCtx ,以此实现一种“继承”。

cancelCtx 源码:
https://github.com/golang/go/blob/76f3e0ac8d094b2bc5f8a3fb8a19d1d17a07fe2c/src/context/context.go#L423

这就是为什么不同的 context 底层结构体( cancelCtx 、timerCtx 、valueCtx……)可以通过 WithCancel 、WithDeadline 、WithValue 等标准库方法组成一个灵活的“context 链”, 还能够基于拼接顺序 "override" 各自的实现方法,第一次看源码的时候觉得还是挺奇妙的。

所以当你有扩展行为实现的需求的时候,在 Go 确实要首先考虑用接口
2024 年 11 月 4 日
回复了 rainbowStay 创建的主题 Go 编程语言 关于 sync.Map 的一点疑问
这里 iface 写错了,interface{} 应该是对应 eface
2024 年 11 月 4 日
回复了 rainbowStay 创建的主题 Go 编程语言 关于 sync.Map 的一点疑问
关键在于 Sync.Map.Store(k, v) ,当你传值为 nil 调用 Store 时,因为 k,v 的类型是 any (也就是 interface{}),在 runtime 也就是结构体 iface ,如果你传入的是 nil ,golang 会用 iface 把这个 nil 包一下,对应的 _type 与 data 应该都是空值(nil),所以你可以在 sync.Map.Store 里面后续的源码里可以看到,value 是可以拿到内存地址的(&value ),因为这个 value 本质上是个 iface 结构体,而直接写 &nil 在 Golang 是会编译报错的。

而 readOnly.m 的值类型是 entry ,entry.p 是一个指向 interface{} 值的指针。当你调用 Sync.Map.Store(k, nil) 时,对应的 entry.p 不会变成 nil ,而是变成一个指向 interface{} (iface) 的指针,这个 iface 相当于包装了值 nil 。

而 Sync.Map.Delete() 就确实会把 entry.p 变成 nil ,所以二者确实是有明确区别的。

写一个函数就可以简单验证,也可以汇编拿出来自己看下:

package main

import "fmt"

func printAnyAddr(v any) {
fmt.Println(&v)
}

func main() {
printAnyAddr(nil)
}
看 go runtime 和 go 标准库的源码能够快速提升 Go 语言的基础理解,而且能学习一套可能是 Go 团队比较中意的实践代码风格(其实很像 C )。

《 Go 语言设计与实现》 - https://draveness.me/golang/
golang-note - https://github.com/cch123/golang-notes/tree/master

这是 Go 语言设计实现源码分析的系列文章可以看下,注意下文章里对应的 Go 版本可能比较老了。不过关于 Go 的 GC 这两个项目没有讲得很全面,要看的话估计还得再找找。

Go 语言是自举的,所以通过学习 Go 语言本身的源码确实提升很快。

K8S 的源码我个人感觉其实风格不是很 Go ,即便用 Java 重新写一套感觉也没啥违和感。我自己最近在看 VictoriaMetrics , 作者应该算是 Go 性能大师了,可以学到很多内存管理、并发控制、IO 优化的内容,里面也包括一些他自己写的轮子:fastcache 、uint64set 等等,都是挺值得琢磨的,欢迎讨论。

看完 VM 以及作者的其他项目(如 fasthttp )我可能会去看 NATS ,因为作为 Go 实现的消息队列它已经实现了很多对标 Kafka 的功能,所以也看看设计,以及思考性能上有没有优化空间。
1  2  3  4  5  6  7  8  9  10 ... 23  
About   ·   Help   ·   Advertise   ·   Blog   ·   API   ·   FAQ   ·   Solana   ·   2678 Online   Highest 6679   ·     Select Language
创意工作者们的社区
World is powered by solitude
VERSION: 3.9.8.5 · 26ms · UTC 12:24 · PVG 20:24 · LAX 05:24 · JFK 08:24
♥ Do have faith in what you're doing.