多线程程序中没有加速

我在玩Go语言并发,发现有些东西对我来说是不透明的。


我写了并行矩阵乘法,也就是说,每个任务计算乘积矩阵的单行,然后将源矩阵的相应行和列相乘。


这是Java程序


public static double[][] parallelMultiply(int nthreads, final double[][] m1, final double[][] m2) {

    final int n = m1.length, m = m1[0].length, l = m2[0].length;

    assert m1[0].length == m2.length;


    double[][] r = new double[n][];


    ExecutorService e = Executors.newFixedThreadPool(nthreads);

    List<Future<double[]>> results = new LinkedList<Future<double[]>>();

    for (int ii = 0; ii < n; ++ii) {

        final int i = ii;

        Future<double[]> result = e.submit(new Callable<double[]>() {

            public double[] call() throws Exception {

                double[] row = new double[l];

                for (int j = 0; j < l; ++j) {

                    for (int k = 0; k < m; ++k) {

                        row[j] += m1[i][k]*m2[k][j];

                    }

                }

                return row;

            }

        });

        results.add(result);

    }

    try {

        e.shutdown();

        e.awaitTermination(1, TimeUnit.HOURS);

        int i = 0;

        for (Future<double[]> result : results) {

            r[i] = result.get();

            ++i;

        }

    } catch (Exception ex) {

        ex.printStackTrace();

        return null;

    }


    return r;

}

这是Go程序


type Matrix struct {

    n, m int

    data [][]float64

}


func New(n, m int) *Matrix {

    data := make([][]float64, n)

    for i, _ := range data {

        data[i] = make([]float64, m)

    }

    return &Matrix{n, m, data}

}


func (m *Matrix) Get(i, j int) float64 {

    return m.data[i][j]

}


func (m *Matrix) Set(i, j int, v float64) {

    m.data[i][j] = v

}


func MultiplyParallel(m1, m2 *Matrix) *Matrix {

    r := New(m1.n, m2.m)


    c := make(chan interface{}, m1.n)

    for i := 0; i < m1.n; i++ {

        go func(i int) {

            innerLoop(r, m1, m2, i)

            c <- nil

        }(i)

    }


    for i := 0; i < m1.n; i++ {

        <-c

    }


    return r

}



烙印99
浏览 235回答 3
3回答

慕容3067478

您可能正在经历错误共享的影响。简而言之,如果两个数据恰好落在同一CPU高速缓存行上,则从在不同CPU内核上执行的线程修改这两个数据将触发昂贵的高速缓存一致性协议。这种缓存“乒乓”非常难以诊断,并且可能在逻辑上完全不相关的数据上发生,只是因为它们恰好位于内存中足够近的位置。100%的CPU负载是虚假共享的典型代表-内核确实在100%工作,它们只是在您的程序上不工作-他们正在同步缓存。在Java程序中,直到有时间将其“集成”到最终结果中之前,您都拥有线程专用数据,这使您免于错误共享。我不熟悉Go,但是从您自己的判断来看,线程直接将数据写入通用数组,这恰恰是可能触发错误共享的一种方式。这是一个示例,在多线程环境中,完全有效的单线程推理如何恰好相反!要对该主题进行更深入的讨论,我热烈推荐Herb Sutter的文章:消除虚假共享,或进行一次演讲:机器体系结构:您的编程语言从不告诉您的事情(以及相关的PDF幻灯片)。

拉莫斯之舞

如果您能够在Linux环境中运行这些代码,则可以使用perf来衡量错误的共享效果。

吃鸡游戏

对于Linux,Windows 32和ditto 64,还有AMD的CodeXL和CodeAnalyst。由于适用的性能寄存器不同,他们将比在Intel上运行的应用程序更详细地描述在AMD处理器上运行的应用程序。
打开App,查看更多内容
随时随地看视频慕课网APP

相关分类

Go