我在玩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;
}
,這是圍棋程序
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
}
func innerLoop(r, m1, m2 *Matrix, i int) {
for j := 0; j < m2.m; j++ {
s := 0.0
for k := 0; k < m1.m; k++ {
s = s + m1.Get(i, k) * m2.Get(k, j)
}
r.Set(i, j, s)
}
}
當我用Java程序來確定nthreads = 1,來確定nthreads = 2有我的雙核近一倍加速N450凌動上網本。 當我使用GOMAXPROCS = 1和GOMAXPROCS = 2的Go程序時,根本沒有加速!
儘管Java代碼使用額外的存儲空間Future
秒,然後collectes它們的值的結果矩陣,而不是在工人的代碼(這就是圍棋的版本一樣)直接序列更新,它執行多快多了幾個核心比Go版本。
特別有趣的是Go版本與GOMAXPROCS = 2加載兩個核心(htop在程序工作時在兩個處理器上顯示100%負載),但計算時間與GOMAXPROCS = 1相同(htop顯示100%只有在這種情況下的一個核心)。另一個值得關注的問題是即使在簡單的單線程乘法中,Java程序的速度也要快於Go程序,但這並不意外(考慮到here的基準)並且不應該影響多核性能乘數。
我在這裏做錯了什麼?有沒有辦法加快Go程序?
UPD: 看來,我發現我在做什麼錯誤。我正在使用System.currentTimeMillis()
檢查java程序的時間,並使用time
shell命令執行Go程序。我錯誤地將來自zsh輸出的「用戶」時間視爲程序工作時間,而不是「全部」時間。現在,我仔細檢查所有的運算速度,這讓我幾乎翻倍加速太(雖然它比Java的slighlty較小):
% time env GOMAXPROCS=2 ./4-2-go -n 500 -q
env GOMAXPROCS=2 ./4-2-go -n 500 -q 22,34s user 0,04s system 99% cpu 22,483 total
% time env GOMAXPROCS=2 ./4-2-go -n 500 -q -p
env GOMAXPROCS=2 ./4-2-go -n 500 -q -p 24,09s user 0,10s system 184% cpu 13,080 total
看來我得更加周到。
仍然Java程序給同一案件五次更少的時間。但是我認爲這是另一個問題。
我編譯和運行你的代碼,並沒有注意到這一點。相反,對於一個1000x1000矩陣的乘法,我看到了預期的加速。你應該發佈一個完整的程序,並提供你如何計時的細節。 – Sonia 2012-04-04 18:56:06
謝謝你的回答。請參閱我的問題更新。 – 2012-04-05 07:18:22