例如簡化的C++,它只會通過矩陣的所有元素,因爲這就是你的嵌套循環所做的。你並不需要計算i*M+j
,爲你的公式不使用I/J,在任何特定的方式,它只是通過所有的元素去一次:
void muldata(float* fxx, const float* fx, const float* fy, const unsigned int M, const unsigned int N) {
int ofs = 0;
do {
fxx[ofs] = fx[ofs] * fy[ofs];
++ofs;
} while (ofs < M*N);
}
會讓clang -O3 -m32
(V4.0.0)產生這樣的:
muldata(float*, float const*, float const*, unsigned int, unsigned int): # @muldata(float*, float const*, float const*, unsigned int, unsigned int)
push ebp
push ebx
push edi
push esi
sub esp, 12
mov esi, dword ptr [esp + 48]
mov edi, dword ptr [esp + 40]
mov ecx, dword ptr [esp + 36]
mov edx, dword ptr [esp + 32]
mov eax, 1
imul esi, dword ptr [esp + 44]
cmp esi, 1
cmova eax, esi
xor ebp, ebp
cmp eax, 8
jb .LBB0_7
mov ebx, eax
and ebx, -8
je .LBB0_7
mov dword ptr [esp + 4], eax # 4-byte Spill
cmp esi, 1
mov eax, 1
mov dword ptr [esp], ebx # 4-byte Spill
cmova eax, esi
lea ebx, [ecx + 4*eax]
lea edi, [edx + 4*eax]
mov dword ptr [esp + 8], ebx # 4-byte Spill
mov ebx, dword ptr [esp + 40]
cmp edx, dword ptr [esp + 8] # 4-byte Folded Reload
lea eax, [ebx + 4*eax]
sbb bl, bl
cmp ecx, edi
sbb bh, bh
and bh, bl
cmp edx, eax
sbb al, al
cmp dword ptr [esp + 40], edi
mov edi, dword ptr [esp + 40]
sbb ah, ah
test bh, 1
jne .LBB0_7
and al, ah
and al, 1
jne .LBB0_7
mov eax, dword ptr [esp] # 4-byte Reload
lea ebx, [edi + 16]
lea ebp, [ecx + 16]
lea edi, [edx + 16]
.LBB0_5: # =>This Inner Loop Header: Depth=1
movups xmm0, xmmword ptr [ebp - 16]
movups xmm2, xmmword ptr [ebx - 16]
movups xmm1, xmmword ptr [ebp]
movups xmm3, xmmword ptr [ebx]
add ebp, 32
add ebx, 32
mulps xmm2, xmm0
mulps xmm3, xmm1
movups xmmword ptr [edi - 16], xmm2
movups xmmword ptr [edi], xmm3
add edi, 32
add eax, -8
jne .LBB0_5
mov eax, dword ptr [esp] # 4-byte Reload
mov edi, dword ptr [esp + 40]
cmp dword ptr [esp + 4], eax # 4-byte Folded Reload
mov ebp, eax
je .LBB0_8
.LBB0_7: # =>This Inner Loop Header: Depth=1
movss xmm0, dword ptr [ecx + 4*ebp] # xmm0 = mem[0],zero,zero,zero
mulss xmm0, dword ptr [edi + 4*ebp]
movss dword ptr [edx + 4*ebp], xmm0
inc ebp
cmp ebp, esi
jb .LBB0_7
.LBB0_8:
add esp, 12
pop esi
pop edi
pop ebx
pop ebp
ret
這是遠遠優於您的代碼(默認情況下包括循環矢量化)。
如果你指定對齊指針並且使M/N編譯時間保持不變,那麼它可能會產生更好的結果。
我只是驗證了C++變種作品將cpp.sh網站,它延伸到這一點:
#include <iostream>
void muldata(float* fxx, const float* fx, const float* fy, const unsigned int M, const unsigned int N) {
unsigned int ofs = 0;
do {
fxx[ofs] = fx[ofs] * fy[ofs];
++ofs;
} while (ofs < M*N);
}
int main()
{
// constexpr unsigned int M = 1;
// constexpr unsigned int N = 1;
// const float fx[M*N] = { 2.2f };
// const float fy[M*N] = { 3.3f };
constexpr unsigned int M = 3;
constexpr unsigned int N = 2;
const float fx[M*N] = { 2.2f, 1.0f, 0.0f,
1.0f, 1.0f, 1e-24f };
const float fy[M*N] = { 3.3f, 3.3f, 3.3f,
5.5f, 1e30f, 1e-24f };
float fr[M*N];
muldata(fr, fx, fy, M, N);
for (unsigned int i = 0; i < N; ++i) {
for (unsigned int j = 0; j < M; ++j) std::cout << fr[i*M+j] << " ";
std::cout << std::endl;
}
}
輸出:
7.26 3.3 0
5.5 1e+30 0
還有的還評論了1x1的輸入數據,這應該是你的案例中第一個要調試的東西。試着讓這個例子在你最喜歡的C++ IDE中工作,然後用你的彙編代碼替換muldata
,並通過它進行調試,看看它發生了什麼。
如果'mulps xmm5,xmm6'爲零,那麼'xmm5'或'xmm6'中的一個爲零。那它是哪一個?你爲什麼不使用C++,它肯定會產生更快的循環,至少它會優化'i * M'等等,再加上它可能會更容易調試和維護。 – Ped7g
其實當然有其他的角落案例,其中float x * float y = 0,即使當x/y都非爲零時,因爲float本身的精確度有限,所以例如'1e-23 * 1e-23 = 0 '等等......如果沒有來自調試器的一些示例數據,就不可能知道你遇到了什麼,如果你看到這些數據,你可能也會看到答案。 – Ped7g
我認爲這個問題不在於寄存器的值。因爲如果我改變istruction mulps與istruction addps代碼工作 –