【问题标题】:How to iterate int range concurrently如何同时迭代int范围
【发布时间】:2017-08-16 19:29:28
【问题描述】:

出于纯粹的教育目的,我创建了一个base58 包。它将使用bitcoin base58 symbol chartuint64 进行编码/解码,例如:

b58 := Encode(100)  // return 2j

num := Decode("2j") // return 100

在创建第一个测试时,我使用this

func TestEncode(t *testing.T) {
    var i uint64
    for i = 0; i <= (1<<64 - 1); i++ {
        b58 := Encode(i)
        num := Decode(b58)
        if num != i {
            t.Fatalf("Expecting %d for %s", i, b58)
        }
    }
}

这种“幼稚”的实现尝试将所有范围从 uint64(从 0 到 18,446,744,073,709,551,615)转换为 base58,然后再转换回 uint64,但需要太多时间。

为了更好地了解go 如何处理并发,我想知道如何使用通道或goroutines 并以最有效的方式在整个 uint64 范围内执行迭代?

是否可以分块并行处理数据,如果可以,如何实现?

提前致谢。

更新

就像@Adrien 的回答中提到的那样,一种方法是使用t.Parallel(),但这仅适用于测试包时,无论如何,通过实现它我发现它明显变慢,它并行运行但没有速度增益。

我知道完成完整的uint64 可能需要数年时间,但我现在想要找到的是通道或 goroutine 如何可能有助于加快进程(小范围测试1&lt;&lt;16)可能通过使用像 https://play.golang.org/p/9U22NfrXeq 这样的东西只是一个例子。

问题不在于如何测试包,而在于什么算法、技术可以通过使用并发来更快地迭代。

【问题讨论】:

  • 请注意,此测试也不能证明您的实现实际上是正确的;只有那个解码/编码匹配。如果他们都以同样的方式错了,测试将通过。您需要针对规范值进行测试以证明其正确性。
  • 如果您的编码/解码可以在一个 CPU 上在一秒钟内完成 10 亿次转换(这是非常乐观的),那么您需要 60 个 CPU 才能在 10 年内完成这项任务。这是假设 goroutines 和 channels 没有开销。
  • @PaulHankin 你是完全正确的,但我的想法是试图理解如何解决这个问题,比如使用通道 goroutines 处理素数时,尽管需要数年时间我想了解更多关于如何优化它.

标签: go concurrency


【解决方案1】:

此功能内置于 Go testing 包中,格式为 T.Parallel

func TestEncode(t *testing.T) {
    var i uint64
    for i = 0; i <= (1<<64 - 1); i++ {
        t.Run(fmt.Sprintf("%d",i), func(t *testing.T) {
            j := i            // Copy to local var - important
            t.Parallel()      // Mark test as parallelizable
            b58 := Encode(j)
            num := Decode(b58)
            if num != j {
                t.Fatalf("Expecting %d for %s", j, b58)
            }
        })
    }
}

【讨论】:

  • 感谢您指点 T.Parrallel,但如果我是对的,请修复示例以使其正常工作,t.Run() 缺少 ) 第二个 Itoa 似乎不适用于 uint64cannot use i (type uint64) as type int in argument to strconv.Itoa
  • 更正了代码示例,但您似乎明白了。
  • 知道为什么并行运行比不运行 t.Parallel() 需要更多时间(更慢)吗?
  • 并行处理存在开销,取决于并行化的操作、硬件、操作系统等,可能会导致总执行时间变慢。
【解决方案2】:

我想出了这个解决方案:

package main

import (
    "fmt"
    "time"

    "github.com/nbari/base58"
)

func encode(i uint64) {
    x := base58.Encode(i)
    fmt.Printf("%d = %s\n", i, x)
    time.Sleep(time.Second)
}

func main() {
    concurrency := 4
    sem := make(chan struct{}, concurrency)
    for i, val := uint64(0), uint64(1<<16); i <= val; i++ {
        sem <- struct{}{}
        go func(i uint64) {
            defer func() { <-sem }()
            encode(i)
        }(i)
    }
    for i := 0; i < cap(sem); i++ {
        sem <- struct{}{}
    }
}

基本上,启动 4 个 worker 并调用 encode 函数,为了更多地注意/理解这种行为,添加了 sleep 以便可以以 4 个块打印数据。

此外,这些答案帮助我更好地理解并发理解:https://stackoverflow.com/a/18405460/1135424

如果有更好的方法请告诉我。

【讨论】:

    猜你喜欢
    • 2020-05-18
    • 1970-01-01
    • 2021-12-04
    • 1970-01-01
    • 1970-01-01
    • 2013-07-08
    • 2012-11-05
    • 1970-01-01
    • 2014-09-08
    相关资源
    最近更新 更多