Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

调研:使用 go-clone 的业务场景 #15

Closed
huandu opened this issue Apr 2, 2023 · 5 comments
Closed

调研:使用 go-clone 的业务场景 #15

huandu opened this issue Apr 2, 2023 · 5 comments
Labels
help wanted Extra attention is needed

Comments

@huandu
Copy link
Owner

huandu commented Apr 2, 2023

大家好,我是这个库的作者,我很想调研一下大家是如何在项目中使用这个库的,请告诉我用法、使用的原因、相关业务场景。

我想通过这样的调研了解这个库的适用范围,从而帮助我更好的发展这个库。我的目标是,在 Go runtime 允许范围内提供一些尽可能高效的内存拷贝 API,满足各种性能敏感场景中的内存管理需求。经过这么多年的研究和积累,特别是通过这个库实践出来的很多有趣的 trick,一定程度上证明了 Go runtime 中内存管理相关机制的可塑性。如果我能更好的理解大家的用法和需求,我认为我可以将内存拷贝和管理这件事情做的更好,让这个库产生更大的价值。

欢迎大家在这个 issue 下面留言,我都会看到并跟大家讨论。感谢大家!

@huandu huandu added the help wanted Extra attention is needed label Apr 2, 2023
@huandu huandu pinned this issue Apr 2, 2023
@molon
Copy link

molon commented Apr 3, 2023

我目前的使用场景比较简单就是 Clone ,非要特别指出的话可能是"偶尔需要做快照之后进行比对?",但貌似也不好给到一个通用 Compare 方法,这点倒无需在意。

其实对于此库,我比较期望的是能有 Copy 方法。
业务中 orm model/pb互转 之类的场景其实还蛮多的,并且往往需要额外指出哪些字段是需要忽略 Copy 的,我目前的方案比较蠢,是利用 json和嵌套结构体以及omitempty 来做的。实际使用中也遇到了一些困难,因为 protobuf 的 json 序列化和常规的还不一样,例如 jsonName/oneof/wellKnownTypes 等等等等吧,因此我还基于 jsoniter 实现了一个类似 protojson 的库。
扯的有点远,总的来说就是我目前使用的方案个人觉得性能不够好,而由于此库是基于反射 Clone ,和 Copy 的实现机制会有些类似,所以会对此有些期待。不过呢,也知道此库不可能会去和 proto 有耦合,所以如果计划实现 Copy 且能开足够灵活的口子让使用者自定义转换就很完美。

关于内存管理,我了解的不多,但是刚刚查看最新的 commit 才发现 allocator 有 sync.Pool 的相关支持,看了ExampleAllocator_syncPool有些疑问:

  1. 为什么 pool 需要作为 allocator 的属性存在?在定义 CustomFunc 的时候自己指定自己定义的 pool 不就可以了吗?并且以Example 来看,貌似目前就只能定义一个 type 的重用,因为 allocator 只有一个 pool。
  2. 既然 sync.Pool 可以通过 runtime.SetFinalizer 去自动管理回收,貌似可以封装一个可自动管理回收的 Pool 库出来?因为以前没想到这么玩,现在倒是很好奇为什么当初 sync.Pool 不直接这样去设计了。

@huandu
Copy link
Owner Author

huandu commented Apr 3, 2023

其实对于此库,我比较期望的是能有 Copy 方法。

这个可以有,不过我感觉超出了 clone 的范畴了,恐怕需要用另一个新的库来实现比较好。当前有几个专门做 copy 的库,不过基本上都是通过 JSON 或 map[string]any 作为媒介进行转化的,我自己都写过一个 github.com/altstory/go-data

做 struct to struct 的 copy 的话,无法用上 go-clone 里面的 unsafe 技巧,基本上都是基于 reflect 的 field to field copy,最多可以用上 sonic 的 JIT 机制。如果有时间我会考虑尝试一下写个原型,可能新开一个项目吧。

为什么 pool 需要作为 allocator 的属性存在?

这是为了让 AllocatorMethods 这个结构尽可能不动态分配内存,避免构造 Allocator 时候产生额外的碎片内存,增加 GC 压力。比如现在实现的 FromArena(arena),在构造 Allocator 的过程中不会在 heap 上申请任何内存,支持 Arena 的 methods 就是一个全局变量。

我觉得使用 Allocator 就是为了精细控制内存分配过程了,那么在构造 Allocator 的过程中别额外制造不可控的碎片内存还是挺重要的。

既然 sync.Pool 可以通过 runtime.SetFinalizer 去自动管理回收,……,现在倒是很好奇为什么当初 sync.Pool 不直接这样去设计了

runtime.SetFinalizer 是有成本的,相比业务主动的调用 Put 肯定效率更低。对于标准库来说,考虑的应该是提供一个性能最大化的解决方案,至于业务怎么玩花活(牺牲性能来提升使用体验),这是业务代码自己的事情,所以我觉得 sync.Pool 现在的设计是合理的。

@molon
Copy link

molon commented Apr 3, 2023

感谢。
其他都能理解了。不过依然还是对这点有不解: Allocator 如何利用 .pool 去管理多个 type 的重用。

@huandu
Copy link
Owner Author

huandu commented Apr 3, 2023

Allocator 如何利用 .pool 去管理多个 type 的重用。

其实 Allocator 并不关心 pool 是什么,只是负责持有这个变量,并且交给 New 等各种方法,由这些方法来使用 pool

要想简单的实现多种 type 的复用,得使用 go1.20 的 Arena 才行,这个里面提供了基于底层 mspan 的真正的强类型内存分配器,这个基本上只能够 runtime 自己来做,业务代码很难越过这一层来做实现,具体的你可以看看 go1.20 Arena 相关的文档和代码。

sync.Pool 并不能实现多种类型的管理,这个东西就被设计成只支持一种类型的缓存而已,我在例子里面写的只是一个抛砖引玉的思路而已。

@huandu
Copy link
Owner Author

huandu commented Apr 18, 2023

将讨论迁移到 #17

@huandu huandu closed this as completed Apr 18, 2023
@huandu huandu unpinned this issue Apr 18, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
help wanted Extra attention is needed
Projects
None yet
Development

No branches or pull requests

2 participants