【问题标题】:Slice chunking in GoGo中的切片分块
【发布时间】:2016-05-12 19:17:33
【问题描述】:

我有一个切片,其中包含约 210 万个日志字符串,我想创建一个切片,字符串尽可能均匀分布。

这是我目前所拥有的:

// logs is a slice with ~2.1 million strings in it.
var divided = make([][]string, 0)
NumCPU := runtime.NumCPU()
ChunkSize := len(logs) / NumCPU
for i := 0; i < NumCPU; i++ {
    temp := make([]string, 0)
    idx := i * ChunkSize
    end := i * ChunkSize + ChunkSize
    for x := range logs[idx:end] {
        temp = append(temp, logs[x])
    }
    if i == NumCPU {
        for x := range logs[idx:] {
            temp = append(temp, logs[x])
        }
    }
    divided = append(divided, temp)
}

idx := i * ChunkSize 将为我提供logs 索引的当前“块开始”,end := i * ChunkSize + ChunkSize 将为我提供“块结束”,或该块范围的结束。我找不到任何关于如何在 Go 中分块/分割切片或迭代有限范围的文档或示例,所以这就是我想出的。但是,它只会多次复制第一个块,所以它不起作用。

我如何(尽可能均匀地)在 Go 中分块?

【问题讨论】:

    标签: go slice chunking


    【解决方案1】:

    您不需要创建新切片,只需将 logs 切片附加到 divided 切片即可。

    http://play.golang.org/p/vyihJZlDVy

    var divided [][]string
    
    chunkSize := (len(logs) + numCPU - 1) / numCPU
    
    for i := 0; i < len(logs); i += chunkSize {
        end := i + chunkSize
    
        if end > len(logs) {
            end = len(logs)
        }
    
        divided = append(divided, logs[i:end])
    }
    
    fmt.Printf("%#v\n", divided)
    

    【讨论】:

    • Ahhhhhhh 这就是我所缺少的。我一直试图在有限的范围内迭代,而不是通过块长度进行迭代。我花了 8 个小时试图弄清楚如何让我的工作,哈哈。感谢您的回答,非常有帮助。
    • 你看起来在divided 的长度上差了一个。例如,numCPU = 3; logs = logs[:8]; chunkSize := len(logs) / numCPU; if chunkSize == 0 { chunkSize = 1 }; 分为 4 个,而不是 3 个,用于 3 个 cpu 和 8 个日志:play.golang.org/p/EdhiclVR0q。对于chunkSize,写chunkSize := (len(logs) + numCPU - 1) / numCPU;play.golang.org/p/xDyFXt45Fz
    • @peterSO:谢谢,只是从原版中复制的,没想到要检查。
    • 如果有人想知道(len(logs) + numCPU - 1) / numCPU 是什么,那只是len(logs)/numCPU 的ceil
    【解决方案2】:

    另一个变种。它的工作速度比JimB 提出的快大约 2.5 倍。测试和基准是here

    https://play.golang.org/p/WoXHqGjozMI

    func chunks(xs []string, chunkSize int) [][]string {
        if len(xs) == 0 {
            return nil
        }
        divided := make([][]string, (len(xs)+chunkSize-1)/chunkSize)
        prev := 0
        i := 0
        till := len(xs) - chunkSize
        for prev < till {
            next := prev + chunkSize
            divided[i] = xs[prev:next]
            prev = next
            i++
        }
        divided[i] = xs[prev:]
        return divided
    }
    

    【讨论】:

    • It works about 2.5 times faster 不幸的是,这无法解释。我的猜测是,更少的 JT 分配。
    • @mh-cbon 主要原因是预分配的切片,因为我们知道它的确切最终大小。它为我们提供了 9 个 allocs/op 而不是 53 个和大部分速度增益
    【解决方案3】:

    对任何 []T 使用反射

    https://github.com/kirito41dd/xslice

    package main
    
    import (
        "fmt"
        "github.com/kirito41dd/xslice"
    )
    
    func main() {
        s := []int{0, 1, 2, 3, 4, 5, 6, 7, 8, 9}
        i := xslice.SplitToChunks(s, 3)
        ss := i.([][]int)
        fmt.Println(ss) // [[0 1 2] [3 4 5] [6 7 8] [9]]
    }
    

    https://github.com/kirito41dd/xslice/blob/e50d91fa75241a3a03d262ad51c8e4cb2ea4b995/split.go#L12

    func SplitToChunks(slice interface{}, chunkSize int) interface{} {
        sliceType := reflect.TypeOf(slice)
        sliceVal := reflect.ValueOf(slice)
        length := sliceVal.Len()
        if sliceType.Kind() != reflect.Slice {
            panic("parameter must be []T")
        }
        n := 0
        if length%chunkSize > 0 {
            n = 1
        }
        SST := reflect.MakeSlice(reflect.SliceOf(sliceType), 0, length/chunkSize+n)
        st, ed := 0, 0
        for st < length {
            ed = st + chunkSize
            if ed > length {
                ed = length
            }
            SST = reflect.Append(SST, sliceVal.Slice(st, ed))
            st = ed
        }
        return SST.Interface()
    }
    

    【讨论】:

      【解决方案4】:
      func chunkSlice(items []int32, chunkSize int32) (chunks [][]int32) {
       //While there are more items remaining than chunkSize...
       for chunkSize < int32(len(items)) {
          //We take a slice of size chunkSize from the items array and append it to the new array
          chunks = append(chunks, items[0:chunkSize])
          //Then we remove those elements from the items array
          items = items[chunkSize:]
       }
       //Finally we append the remaining items to the new array and return it
       return append(chunks, items)
      }
      

      视觉示例

      假设我们要将一个数组分成 3 个块

      items:  [1,2,3,4,5,6,7]
      chunks: []
      
      items:  [1,2,3,4,5,6,7]
      chunks: [[1,2,3]]
      
      items:  [4,5,6,7]
      chunks: [[1,2,3]]
      
      items:  [4,5,6,7]
      chunks: [[1,2,3],[4,5,6]]
      
      items:  [7]
      chunks: [[1,2,3],[4,5,6]]
      
      items:  [7]
      chunks: [[1,2,3],[4,5,6],[7]]
      return
      

      【讨论】:

      • 虽然此代码可能会回答问题,但提供有关它如何和/或为什么解决问题的额外上下文将提高​​答案的长期价值。您可以在帮助中心找到更多关于如何写好答案的信息:stackoverflow.com/help/how-to-answer。祝你好运?
      猜你喜欢
      • 2019-12-22
      • 1970-01-01
      • 1970-01-01
      • 2014-01-31
      • 1970-01-01
      • 2017-11-02
      • 1970-01-01
      • 2019-08-09
      • 1970-01-01
      相关资源
      最近更新 更多