用Go Channels实现Cannon算法遇串行化问题,如何优化并发?
Go实现Cannon算法的并发优化方案
当前代码的核心问题
- 全局互斥锁
mu若在multiplyConcurrent中被不当使用,会直接导致所有goroutine串行执行,完全丧失并发优势。 - 矩阵采用值传递,大矩阵的拷贝开销会远超过并发带来的性能提升,甚至拖慢整体速度。
- 并发粒度设计不合理:按步骤启动goroutine的方式,不符合Cannon算法"处理器节点并行运算"的核心逻辑,无法充分利用多核资源。
具体优化步骤
1. 移除不必要的全局锁
Cannon算法中每个计算节点的操作是完全独立的,不需要全局互斥锁保护。如果multiplyConcurrent里用了mu.Lock()/mu.Unlock(),直接删除锁逻辑,让每个goroutine操作独立的内存区域。
2. 改用矩阵引用传递
将矩阵的传递改为指针类型,避免大矩阵的拷贝开销:
// 修改函数参数为指针类型 func multiplyConcurrent(a *matrixType, b *matrixType, step int, resultCh chan<- matrixResult, wg *sync.WaitGroup) { defer wg.Done() // 计算逻辑... } // 调用时传递指针 go multiplyConcurrent(&matrixA, &matrixB, i, resultCh, &wg)
3. 调整并发粒度:按矩阵块分配goroutine
Cannon算法的正确并发方式是为每个矩阵块(对应处理器网格的节点)启动一个goroutine,每个goroutine负责完成自身块的所有移位和乘法累加操作,而非按步骤启动goroutine。示例核心逻辑:
func CannonOptimized(A, B matrixType) matrixType { n := len(A) result := make(matrixType, n) for i := range result { result[i] = make([]int, n) } var wg sync.WaitGroup // 为每个矩阵元素(或块)启动goroutine for i := 0; i < n; i++ { for j := 0; j < n; j++ { wg.Add(1) go func(x, y int) { defer wg.Done() sum := 0 // 执行n步移位与点积计算 for s := 0; s < n; s++ { for k := 0; k < n; k++ { sum += A[x][(k+s)%n] * B[(k+s)%n][y] } } result[x][y] = sum }(i, j) } } wg.Wait() return result }
4. 通道使用优化
可以直接让goroutine写入结果矩阵的对应位置(无竞争),避免通道的额外开销。如果必须使用通道,确保通道是带缓冲的,且不要在计算逻辑中阻塞发送操作。
5. 修复循环变量捕获问题
原循环中启动goroutine时,若goroutine延迟执行可能捕获到旧的matrixA/matrixB值,可在循环内部创建局部变量捕获当前值:
for i := 0; i < row; i++ { wg.Add(1) currentA := matrixA currentB := matrixB go multiplyConcurrent(¤tA, ¤tB, i, resultCh, &wg) matrixA = alignHorizontalByOne(matrixA, 1) matrixB = alignVerticalByOne(matrixB, 1) }
内容的提问来源于stack exchange,提问作者Kbalsamy
相关产品推荐
相关产品推荐

