为了账号安全,请及时绑定邮箱和手机立即绑定

在 Go 中处理包分配的最佳实践

在 Go 中处理包分配的最佳实践

Go
凤凰求蛊 2021-06-17 17:01:09
我正在编写一个包,它在内部大量使用缓冲区进行临时存储。我有一个全局(但未导出)字节切片,它从 1024 个元素开始,并根据需要加倍增长。但是,很可能我的包的用户会以这样的方式使用它,导致分配大缓冲区,然后停止使用该包,从而浪费大量分配的堆空间,我没有办法知道是否释放缓冲区(或者,因为这是 Go,让它被 GC 处理)。我已经想到了三种可能的解决方案,但没有一个是理想的。我的问题是:在这种情况下,这些解决方案中的任何一个,或者可能是我没有想到的,是否是标准做法?有没有标准做法?还有其他想法吗?算了。那好吧。处理这个太难了,留下分配的内存并没有那么糟糕。这种方法的问题很明显:它没有解决问题。导出“我完成了”或“缩小内存使用量”功能。导出一个用户可以调用的函数(智能调用显然取决于他们),这将释放包使用的内部存储空间。这种方法的问题是双重的。首先,它为用户提供了一个更复杂、更不干净的界面。其次,用户可能无法或不切实际地知道何时调用这样的函数是明智的,因此无论如何它都可能毫无用处。运行一个 goroutine,它会在包闲置一段时间后释放缓冲区,或者在缓冲区大小在一段时间内没有增加时缩小缓冲区(可能将长度减半)。这种方法的问题主要在于它给调度程序带来了不必要的压力。显然,单个 goroutine 还不错,但如果这是公认的做法,那么如果您导入的每个包都在幕后执行此操作,它就不会很好地扩展。此外,如果您有一个时间敏感的应用程序,您可能不希望代码在您不知道的情况下运行(也就是说,您可能会假设该包在其函数未被调用时没有执行任何工作 - 一个合理的假设,我会说)。所以……有什么想法吗?
查看完整描述

3 回答

?
慕沐林林

TA贡献2016条经验 获得超9个赞

一种常见的方法是让客户端将现有的 []byte(或其他)作为参数传递给某个调用/函数/方法。例如:


// The returned slice may be a sub-slice of dst if dst was large enough

// to hold the entire encoded block. Otherwise, a newly allocated slice

// will be returned. It is valid to pass a nil dst.

func Foo(dst []byte, whatever Bar) (ret []byte, err error)


另一种方法是从缓存和/或例如池中获取新的 [] 字节(如果您更喜欢该概念的后一个名称),并依靠客户端将使用过的缓冲区返回到此类“回收站”。


顺便说一句:考虑到这一点,你做得对。在可以合理地重用 []byte 缓冲区的地方,有可能降低 GC 负载,从而使您的程序性能更好。有时差异可能很关键。


查看完整回答
反对 回复 2021-06-21
?
慕码人2483693

TA贡献1860条经验 获得超9个赞

您可以在每次操作结束时重新划分缓冲区。

buffer = buffer[:0]

然后,extendAndSliceBuffer如果需要增长,您的函数将具有最有可能可用的原始后备数组。如果没有,您将遭受新的分配,而您无论如何都可能会得到它extendAndSliceBuffer

总的来说,我认为更简洁的解决方案是像@jnml 所说的那样做,让用户在关心性能时传递他们自己的缓冲区。如果他们不关心性能,那么您不应该使用全局变量,而是根据需要简单地分配缓冲区,并在超出范围时将其释放。


查看完整回答
反对 回复 2021-06-21
?
料青山看我应如是

TA贡献1772条经验 获得超8个赞

我有一个全局(但未导出)字节切片,它从 1024 个元素开始,并根据需要加倍增长。

这就是你的问题。你的包中不应该有这样的全局变量。

通常最好的方法是使用带有附加功能的导出结构。缓冲区应驻留在未导出的结构中。这样用户就可以实例化它并让垃圾收集器在他们放手时清理它。

您还希望避免需要这样的全局变量,因为它会妨碍单元测试。单元测试应该能够像用户一样实例化导出的结构,并且每次测试时都这样做。

还取决于什么样的缓冲你的需要,bytes.Buffer因为它已经提供了可能是有用的io.Readerio.Writer功能。bytes.Buffer还会自动增长和缩小其缓冲区。在buffer.go 中,您会看到各种调用以b.Truncate(0)“重置以恢复空间”注释进行收缩。


查看完整回答
反对 回复 2021-06-21
  • 3 回答
  • 0 关注
  • 218 浏览
慕课专栏
更多

添加回答

举报

0/150
提交
取消
意见反馈 帮助中心 APP下载
官方微信