我無法做矩陣,矩陣乘法與上交所C.SSE矩陣,矩陣乘法
這是我走到這一步:
#define N 1000
void matmulSSE(int mat1[N][N], int mat2[N][N], int result[N][N]) {
int i, j, k;
__m128i vA, vB, vR;
for(i = 0; i < N; ++i) {
for(j = 0; j < N; ++j) {
vR = _mm_setzero_si128();
for(k = 0; k < N; k += 4) {
//result[i][j] += mat1[i][k] * mat2[k][j];
vA = _mm_loadu_si128((__m128i*)&mat1[i][k]);
vB = _mm_loadu_si128((__m128i*)&mat2[k][j]); //how well does the k += 4 work here? Should it be unrolled?
vR = _mm_add_epi32(vR, _mm_mul_epi32(vA, vB));
}
vR = _mm_hadd_epi32(vR, vR);
vR = _mm_hadd_epi32(vR, vR);
result[i][j] += _mm_extract_epi32(vR, 0);
}
}
}
我似乎無法使它給出正確的結果。我錯過了什麼嗎? 和搜索dosent似乎幫助不大 - 每個結果要麼只是做4X4矩陣,墊VEC或一些特殊的魔力,那不是很有可讀性,很難理解......
更新: Woho!我終於弄明白了。除了我的邏輯中的錯誤(感謝Peter Cordes的幫助),還有_mm_mul_epi32()不能正常工作 - 我應該一直使用_mm_mullo_epi32()來代替!
我知道這不是最有效的代碼,但它是爲了讓它正常工作 - 現在我可以繼續優化它。
void matmulSSE(int mat1[N][N], int mat2[N][N], int result[N][N]) {
int i, j, k;
__m128i vA, vB, vR, vSum;
for(i = 0; i < N; ++i) {
for(j = 0; j < N; ++j) {
vR = _mm_setzero_si128();
for(k = 0; k < N; k += 4) {
//result[i][j] += mat1[i][k] * mat2[k][j];
vA = _mm_loadu_si128((__m128i*)&mat1[i][k]);
vB = _mm_insert_epi32(vB, mat2[k][j], 0);
vB = _mm_insert_epi32(vB, mat2[k + 1][j], 1);
vB = _mm_insert_epi32(vB, mat2[k + 2][j], 2);
vB = _mm_insert_epi32(vB, mat2[k + 3][j], 3);
vR = _mm_mullo_epi32(vA, vB);
vR = _mm_hadd_epi32(vR, vR);
vR = _mm_hadd_epi32(vR, vR);
result[i][j] += _mm_extract_epi32(vR, 0);
//DEBUG
//printf("vA: %d, %d, %d, %d\n", vA.m128i_i32[0], vA.m128i_i32[1], vA.m128i_i32[2], vA.m128i_i32[3]);
//printf("vB: %d, %d, %d, %d\n", vB.m128i_i32[0], vB.m128i_i32[1], vB.m128i_i32[2], vB.m128i_i32[3]);
//printf("vR: %d, %d, %d, %d\n", vR.m128i_i32[0], vR.m128i_i32[1], vR.m128i_i32[2], vR.m128i_i32[3]);
//printf("\n");
}
}
}
}
更新2:轉換彼得斯例如到第i-K-j循環順序版本。對於vR需要額外的負載並將存儲器移到內部循環,但設置vA可以向上移動一個循環。結果更快。
void matmulSSE_2(int mat1[N][N], int mat2[N][N], int result[N][N]) {
int i, j, k;
__m128i vA, vB, vR;
for(i = 0; i < N; ++i) {
for(k = 0; k < N; ++k) {
vA = _mm_set1_epi32(mat1[i][k]);
for(j = 0; j < N; j += 4) {
//result[i][j] += mat1[i][k] * mat2[k][j];
vB = _mm_loadu_si128((__m128i*)&mat2[k][j]);
vR = _mm_loadu_si128((__m128i*)&result[i][j]);
vR = _mm_add_epi32(vR, _mm_mullo_epi32(vA, vB));
_mm_storeu_si128((__m128i*)&result[i][j], vR);
//DEBUG
//printf("vA: %d, %d, %d, %d\n", vA.m128i_i32[0], vA.m128i_i32[1], vA.m128i_i32[2], vA.m128i_i32[3]);
//printf("vB: %d, %d, %d, %d\n", vB.m128i_i32[0], vB.m128i_i32[1], vB.m128i_i32[2], vB.m128i_i32[3]);
//printf("vR: %d, %d, %d, %d\n", vR.m128i_i32[0], vR.m128i_i32[1], vR.m128i_i32[2], vR.m128i_i32[3]);
//printf("\n");
}
}
}
}
你有什麼麻煩? –
@RushyPanchal我沒有得到正確的結果。對不起,我應該在我的帖子中指定... – Erlisch
對於你來說調用者是否爲''結果[]'?如果沒有,你應該先做!還要注意,在最內部循環內部進行水平求和是非常可怕的。如果你在同一個最內層循環中爲'result [i] [j]'做了所有的數學運算,只要做'result = hsum(vR)',而不是'+ ='。 hsum是一個水平和函數,可以移植到非MSVC(如果重要的話),並且比編譯器爲您寫的內容可能產生的更少。見http://stackoverflow.com/questions/6996764/fastest-way-to-do-horizontal-float-vector-sum-on-x86,我的答案提到整數hsums。 –