Читаю щас Интеловские мануалы. Выяснил, что обращение к памяти уже не такая тормозная операция - в Intel Core процах, что после четвёртого пня, если данные в каше, то их чтение занимает порядка десятка-двух тактов (а на Core i3,i5,i7 - три такта). Однако это реализовано в виде запроса - пока данные не требуются для операций над ними, последующие независимые от них команды могут исполняться. Интересно также, что запросов может быть съаккумулировано до 8 штук, отсылок на запись - до 4-х.
Касательно параллельного исполнения команд. Теоретический максимум - четыре микрокоманды за такт благодаря нескольким исполнительным АЛУ на чипе, и четырём декодерам (АЛУ, конвертирующим ассемблеровские инструкции в набор микрокоманд для конкретного ядра). Однако среднестатистическая инструкция обычно раскладывается в 2 микрокоманды. Плюс если результат операций одной команды требуется следующей, то параллельность исполнений блокируется. Плюс за такт может быть декодировано не более одной сложной инструкции (которая раскладывается на 2 и более микрокоманд) и три простых (которые переводятся в одну микрокоманду). Поскольку простых инструкций - единицы, то в результате быстродействие всей каракатицы всё так же может быть оценено лейбой с гигагерцами и количеством инструкций в коде.
Но так или иначе, если верить моему последнему тесту функции трансформации, мне удалось 40 раз трансформировать 10 млн вершин за 1016 мс в сумме (среднестатистический результ), при этом за цикл обрабатывалось 4 вершины, и асм-инструкций в цикле - 52. Другими словами, эквивалентное быстродействие - 5.2 ГГц замест 3.2 по паспорту. Т.е. в среднем исполнялось по 2 инструкции за такт. Вот код (работает лишь для 4х количества вершин, т.е. функция не дописана):
Код:
void __declspec(naked) __stdcall
TransformVertices3fx4(const void *Matrix4x4f_ColumnMajor,
const void *ArrayOfVec3f_Source,
void *ArrayOfVec3f_Destination,
unsigned int VertexCount){
_asm{
//Load pointers and counter
mov eax,[esp+4]; //Move pointer to the matrix into accumulator
mov esi,[esp+8]; //Move pointer to the source array into esi
mov edi,[esp+12];//Move pointer to the destination array into edi
mov ecx,[esp+16];//Move quantity of vectors into counter register
//Load matrix
movups xmm0,[eax ]; //Load {m[0],m[1],m[2],m[3]} into xmm0
movups xmm1,[eax+16]; //Load {m[4],m[5],m[6],m[7]} into xmm1
movups xmm2,[eax+32]; //Load {m[8],m[9],m[10],m[11]} into xmm2
movups xmm3,[eax+48]; //Load {m[12],m[13],m[14],m[15]} into xmm3
//Start of vertex transformation' cycle
LABEL_CYCLE_4_START:
movups xmm4,[esi]; //Load {x1,y1,z1,x2} into xmm4
//xmm4:{x1,y1,z1,x2}; xmm5:{-,-,-,-}; xmm6:{-,-,-,-}; xmm7:{-,-,-,-}
pshufd xmm6,xmm4,00000000b;//Load {x1,x1,x1,x1} into xmm6
mulps xmm6,xmm0; //Mult {m0...m3} and x1
addps xmm6,xmm3; //xmm6: {m0...m3}*x1 + {m12...m15}
pshufd xmm7,xmm4,01010101b;//Load {y1,y1,y1,y1} into xmm7
mulps xmm7,xmm1; //Mult {m4...m7} and y1
addps xmm6,xmm7; //xmm6: {m0...m3}*x1 + {m4...m7}*y1 + {m12...m15}
pshufd xmm7,xmm4,10101010b;//Load {z1,z1,z1,z1} into xmm7
mulps xmm7,xmm2; //Mult {m8...m11} and z1
addps xmm6,xmm7; //xmm6: {X1,Y1,Z1,W1}
movups xmm5,[esi+16];//Load {y2,z2,x3,y3} into xmm5
//xmm4:{-,-,-,x2}; xmm5:{y2,z2,x3,y3}; xmm6:{X1,Y1,Z1,-}; xmm7:{-,-,-,-}
shufps xmm4,xmm4,11111111b;//Load {x2,x2,x2,x2} into xmm4
mulps xmm4,xmm0; //Mult {m0...m3} and x2
addps xmm4,xmm3; //xmm4: {m0...m3}*x2 + {m12...m15}
pshufd xmm7,xmm5,00000000b;//Load {y2,y2,y2,y2} into xmm7
mulps xmm7,xmm1; //Mult {m4...m7} and y2
addps xmm4,xmm7; //xmm4: {m0...m3}*x2 + {m4...m7}*y2 + {m12...m15}
pshufd xmm7,xmm5,01010101b;//Load {z2,z2,z2,z2} into xmm7
mulps xmm7,xmm2; //Mult {m8...m11} and z2
addps xmm7,xmm4; //xmm7: {X2,Y2,Z2,W2}
//xmm4:{-,-,-,-}; xmm5:{-,-,x3,y3}; xmm6:{X1,Y1,Z1,-}; xmm7:{X2,Y2,Z2,-}
movups xmm4,[esi+32]; //Load {z3,x4,y4,z4} into xmm4
shufps xmm6,xmm6,00100100b;//Get {X1,Y1,Z1, X1} in xmm6
movss xmm6,xmm7; //Get {X2,Y1,Z1, X1} in xmm6
shufps xmm6,xmm6,00100111b;//Get {X1,Y1,Z1, X2} in xmm6
add esi,48; //Advance pointer to the source array by 48 bytes
movups [edi],xmm6; //Store {X1,Y1,Z1,X2} into destination buffer
//xmm4:{z3,x4,y4,z4}; xmm5:{-,-,x3,y3}; xmm6:{-,-,-,-}; xmm7:{-,Y2,Z2,-}
pshufd xmm6,xmm5,10101010b;//Load {x3,x3,x3,x3} into xmm6
mulps xmm6,xmm0; //Mult {m0...m3} and x3
addps xmm6,xmm3; //xmm6: {m0...m3}*x3 + {m12...m15}
shufps xmm5,xmm5,11111111b;//Load {y3,y3,y3,y3} into xmm5
mulps xmm5,xmm1; //Mult {m4...m7} and y3
addps xmm5,xmm6; //xmm5: {m0...m3}*x3 + {m4...m7}*y3 + {m12...m15}
pshufd xmm6,xmm4,00000000b;//Load {z3,z3,z3,z3} into xmm6
mulps xmm6,xmm2; //Mult {m8...m11} and z3
addps xmm6,xmm5; //Get {X3,Y3,Z3,W3} in xmm6
//xmm4:{-,x4,y4,z4}; xmm5:{-,-,-,-}; xmm6:{X3,Y3,Z3,-}; xmm7:{-,Y2,Z2,-}
shufps xmm7,xmm6,01001001b;//Get {Y2,Z2,X3,Y3} in xmm7
movups [edi+16],xmm7; //Store {Y2,Z2,X3,Y3} into destination buffer
//xmm4:{-,x4,y4,z4}; xmm5:{-,-,-,-}; xmm6:{-,-,Z3,-}; xmm7:{-,-,-,-}
pshufd xmm5,xmm4,01010101b;//Load {x4,x4,x4,x4} into xmm5
mulps xmm5,xmm0; //Mult {m0...m3} and x4
addps xmm5,xmm3; //xmm5: {m0...m3}*x4 + {m12...m15}
pshufd xmm7,xmm4,10101010b;//Load {y4,y4,y4,y4} into xmm7
mulps xmm7,xmm1; //Mult {m4...m7} and y4
addps xmm5,xmm7; //xmm5: {m0...m3}*x4 + {m4...m7}*y4 + {m12...m15}
shufps xmm4,xmm4,11111111b;//Load {z4,z4,z4,z4} into xmm4
mulps xmm4,xmm2; //Mult {m8...m11} and z4
addps xmm5,xmm4; //Get {X4,Y4,Z4,W4} in xmm5
//xmm4:{-,-,-,-}; xmm5:{X4,Y4,Z4,-}; xmm6:{-,-,Z3,-}; xmm7:{-,-,-,-}
movss xmm6,xmm5; //Get {X4,-,Z3,-} in xmm6
shufps xmm6,xmm5,10010010b;//Get {Z3,X4,Y4,Z4} in xmm6
movups [edi+32],xmm6; //Store {Z3,X4,Y4,Z4} into destination buffer
add edi,48; //Advance pointer to the destination array by 48 bytes
sub ecx,4;
jnz LABEL_CYCLE_4_START; //Jump to the beginning of the cycle
//Exit
LABEL_EXIT:
ret 16; //Return from function cleaning up the stack
}
}
//______________________________________________________________________________