如何决定并发操作的数量?

我目前正在编写一个编码器,并且(显然)想让它变得更快。

我有一个工作系统来进行编码(所以每个 goroutine 都在做同样的事情),但我正在努力寻找合适数量的 goroutine 来运行代码。我基本上想决定保持最大数量的 Goroutines CPU 忙。

下面的想法掠过我的脑海:

  • 如果一个文件只有 <1 kB,那么在很多 goroutine 中运行代码是没有用的

  • goroutines 的数量应该受可用的核心/线程的影响

    • 在 4x4 GHz CPU 上运行 16 个 goroutine 不会有问题,但在 4x1 GHz CPU 上呢?

    • 难以确定可靠的跨平台

  • CPU 应该很忙,但不要忙到阻止其他程序响应(~70-ish %?)

    • 由于时钟速度和其他参数,很难事先决定

现在我尝试根据这些因素来决定使用多少个 goroutine,但我不太确定如何跨平台并以可靠的方式这样做。

已经尝试过:

  • 使用线性函数根据文件大小确定

    • 需要基于 CPU 的不同功能

  • 从 lscpu 解析 CPU 规格

    • 不跨平台

    • 需要另一个函数来根据频率确定

这并不令人满意。


海绵宝宝撒
浏览 141回答 1
1回答

长风秋雁

你在评论中提到每个 goroutine 都在读取要编码的文件但当然,文件——任何文件——已经以某种方式编码:也许是纯文本,或者是 UTF-8(字节流),也许是组装成“行”单元。或者它可能是一个图像流,例如一个 mpeg 文件,由一些帧组成。或者它可能是一个由记录组成的数据库。无论其输入形式是什么,它都包含某种基本单元,您可以将其提供给您的(重新)编码器。那个单位,不管它是什么,都是一个合理的分工场所。(多么明智,取决于它是什么。请参阅下面的分块的想法。)假设文件由独立的行组成:然后使用scanner.Scan来读取它们,并将每一行传递给一个接收行的通道。分拆 N,对于一些 N,阅读频道的读者,一次一行:ch := make(chan string)for i := 0; i < n; i++ {&nbsp; &nbsp; go readAndEncode(ch)}// later, or immediately:for s := bufio.NewScanner(os.Stdin); s.Scan(); {&nbsp; &nbsp; ch <- s.Text()}close(ch)如果有 100 行和 4 个阅读器,前四个ch <- s.Text()操作会快速进行,第五个操作会暂停,直到其中一个阅读器完成编码并返回读取通道。如果单个行太小,也许您应该一次读取一个“块”(例如,1 MB)。如果块的末尾有部分行,请备份或阅读更多内容,直到您有整行。然后发送整个数据块。因为通道复制数据,您可能希望发送对块的引用。1 这适用于任何更大的数据单元。(行往往很短,与首先使用通道的开销相比,复制它们的开销通常不是很大。如果你的行有 type string,那么,请参阅脚注。)如果行或行块在这里不是正确的工作单元,请弄清楚什么是. 将 goroutine 想象为每个人都有一份工作要做的人(或忙碌的小地鼠) 。他们可以依靠其他人——另一个人或地鼠——来做一份较小的工作,不管那是什么;并且有十个人,或称地鼠,处理子任务允许主管管理它们。如果你需要做 N 次相同的工作,并且 N 不是无界的,你可以分拆 N 个 goroutine。如果 N可能是无界的,则分拆一个固定的数字(可能基于#cpus)并通过通道为它们提供工作。1正如 Burak Serdar 所指出的,一些副本可以自动删除:例如,字符串实际上是只读切片。切片类型包含三个部分:指向底层数据的指针(引用)、长度和容量。复制切片会复制这三个部分,但不会复制底层数据。字符串也是如此:字符串标题省略了容量,因此通过通道发送字符串仅复制两个标题词。因此,许多明显且易于编码的数据分块方式已经非常有效。
打开App,查看更多内容
随时随地看视频慕课网APP

相关分类

Go