V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
V2EX  ›  grzhan  ›  全部回复第 1 页 / 共 20 页
回复总数  384
1  2  3  4  5  6  7  8  9  10 ... 20  
@qiuhang 其实我也猜测是这样,团队里没有专业一些的工程师。
但类似的专业度不足的问题过去几年已经出现过好几次了( helang 、掰天线……),公司应该是盈利的,他的人脉哪怕只是找个顾问把个关应该也不难(不找其他 up 主,找个大厂上班的老同学或者学长之类也成吧),到现在还没解决这个问题只能说 emmm……
主要还是营销技术宅人设带来的反噬。

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

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

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

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

Go 的语法细节确实有很多实际运行起来不符合直觉的地方,很多时候一定要结合它的内部机制甚至看源码才能顺畅理解, 虽然这些机制大部分理解了也确实比较简单吧(所谓“大道至简”…
7 天前
回复了 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 发展要加进这个特性工作量大了就懒得搞了。
draveness 大佬的《 Go 语言设计与实现》我之前一直看: https://draveness.me/golang/
他应该就是先写系列文章( gitbook ),在收获比较高的访问量与关注度之后,出版了实体书。
我觉得是个路子,然后像 draveness 会画很多配图(确定好配色后用 sketch 画),对于这种源码分析读物的可读性提升了很多,但也更费功夫。
供参考。
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 确实要首先考虑用接口
16 天前
回复了 rainbowStay 创建的主题 Go 编程语言 关于 sync.Map 的一点疑问
这里 iface 写错了,interface{} 应该是对应 eface
16 天前
回复了 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 的功能,所以也看看设计,以及思考性能上有没有优化空间。
说起来这个 Rob Pike 的素数筛法应该思路就是当年 CSP 1978 这篇论文提到的素数算法,以前欧长坤老板做过 CSP 1978 论文解读的分享,里面就有素数筛法的例子,论文当然是自己的 CSP 语言,而他写了个 Go 版本:
https://github.com/changkun/pkg/blob/f787593b4467793f8ee0b07583ea9ffde5adf2be/csp/csp.go#L833
这应该单纯是个闭包问题:

1. ch 变量本质上是个 *hchan ( https://github.com/golang/go/blob/ed035af7b7d7c1cd2e6f852e22a9b04fc2a2cc65/src/runtime/chan.go#L34 ),是指向 make(chan int) 也就是 runtime.makechan 创建的 hchan 的指针。

2. 直接使用闭包引用 ch ,ch 发生了内存逃逸(因为是 go func ,我 -gcflags="-m" 看了下确实逃逸到了堆),你在闭包 go func 中使用 ch ,就是在操作 ch 指向的 hchan 。

3. main 函数后续 ch = ch1 , 也就是 ch 指向了原本 ch1 指向的 hchan ,导致 go func 操作的 hchan 也发生了变化,进而整体程序没有按照预期执行。

4. 而正确的实现 Generate(ch),以及 func(ch){...}(ch) ,是函数传参,是将 ch 变量值复制传入到了 Generate 或者 go func 里。

如果你去看汇编,这次 ch 就分配在栈里( main.ch+40(SP) ),在运行 go func 之前就把它的值复制到寄存器里供函数使用了 ( MOVQ main.ch+40(SP), CX )。因此后续 main 里 ch 针对 hchan 的指向发生变化不会影响 go func 内部,值复制保证 go func 内部指向的 hchan 不变。


感兴趣自己可以看下汇编研究下,我也只是大致看了下:go tool compile -S main.go > assembly.s
看需求更接近开发一个上报监控指标的 agent ,逻辑上应该比较简单。
解决资源问题(内存)可以看下 tinygo ,算是针对嵌入式环境的一个 go 编译器实现,如果没有用不到 go 关键字、反射等特性的话应该问题不大。
中午饭吃饱,下午喝些茶、咖啡,或者水
感觉大的可能的收益是在二进制序列化协议( protobuf ),一般外部接口感觉是在传输 payload 比较大的时候可能会考虑使用,类似 Prometheus Remote Write API , 一次请求可能是几 MB 或者几十 MB 的写入,Protobuf + Snappy 压缩,也可以像 VictoriaMetrics 使用 zstd 进行压缩,当然传输协议其实还是 HTTP 。

我不是做移动端的,看了下 Android 的文档也确实有 gRPC 方案的文档介绍( https://developer.android.com/guide/topics/connectivity/grpc ),但也不是主推的样子。加上上面很多老板提到的调试成本、学习门槛,以及弱网下 grpc 可能会存在坑,导致看下来 grpc 在客户端通信通用场景的优势并不明显。
27 天前
回复了 kneo 创建的主题 程序员 急需一个久坐监控
和楼上说的差不多。

MediaPipe 有 Google 开箱即用的人脸识别方案:
https://ai.google.dev/edge/mediapipe/solutions/vision/face_detector?hl=zh-cn
https://ai.google.dev/edge/mediapipe/solutions/vision/face_landmarker/web_js?hl=zh-cn

然后你检测摄像头有人脸(或者划定一个关注识别的 rect 里面有人脸的话,基于识别到的特征点来判定)就认为你坐着,然后过几十分钟就弹出来提示。
1  2  3  4  5  6  7  8  9  10 ... 20  
关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   实用小工具   ·   3440 人在线   最高记录 6679   ·     Select Language
创意工作者们的社区
World is powered by solitude
VERSION: 3.9.8.5 · 30ms · UTC 10:46 · PVG 18:46 · LAX 02:46 · JFK 05:46
Developed with CodeLauncher
♥ Do have faith in what you're doing.