Вы используете код с плавающей запятой! Компиляторы - мусор с кодом с плавающей запятой.
Вот некоторые измерения, которые я сделал, я использую DevStudio 2005 с оптимизацией по умолчанию и немного изменил код:
// added to the inner part of the loop
fn_value += j;
// added a dependancy on fn_value so that the compiler doesn't optimise the
// whole code down to nothing
printf("Time taken %lf - %f", (double) (end-start) / CLOCKS_PER_SEC, fn_value);
Итак, я запустил это примерно через 5 секунд.
Теперь я немного изменил код:
# include <stdio.h>
# include <time.h>
int main(int argc, char **argv)
{
int fn_value=0;
int n=10,i,j;
unsigned int k;
clock_t start, end;
start = clock();
for(k=0;k<9765625;k++)
{
for(i=0;i<n;i++)
{
for(j=i;j<n;j++)
fn_value+=j;
}
}
end= clock();
printf("Time taken %lf - %d", (double) (end-start) / CLOCKS_PER_SEC, fn_value);
return 0;
}
Я изменил значение fn_value на int. Теперь это занимает около секунды! Таким образом, между добавлением целых и добавлением чисел с плавающей запятой уходит четыре секунды Затем я написал версию с кодами операций IA32 FPU вместо кода C и получил примерно 1,4 секунды, что не намного медленнее, чем использование целых чисел.
Затем я использовал версию с плавающей запятой C, но сделал fn_value удвоенным, и время стало 1,25 с. Это меня удивило. Он превзошел версию кода операции FPU, но, глядя на разборку, единственное отличие заключается в том, что версия на чистом C развернула внутренний цикл.
Кроме того, при использовании чисел с плавающей точкой результат является неверным.
Вот мой последний тестовый код:
# include <stdio.h>
# include <time.h>
void p1 ()
{
double fn_value=0;//if this is a float, the answer is slightly wrong
int n=10,i,j;
unsigned int k;
clock_t start, end;
start = clock();
__asm fldz;
for(k=0;k<9765625;k++)
{
for(i=0;i<n;i++)
{
for(j=i;j<n;j++)
__asm {
fiadd j
}
}
}
__asm fstp fn_value;
end= clock();
printf("p1: Time taken %lf - %lf\n", (double) (end-start) / CLOCKS_PER_SEC, (double) fn_value);
}
void p2 ()
{
double fn_value=0;
int n=10,i,j;
unsigned int k;
clock_t start, end;
start = clock();
for(k=0;k<9765625;k++)
{
for(i=0;i<n;i++)
{
for(j=i;j<n;j++)
fn_value+=j;
}
}
end= clock();
printf("p2: Time taken %lf - %lf\n", (double) (end-start) / CLOCKS_PER_SEC, (double) fn_value);
}
void p3 ()
{
float fn_value=0;
int n=10,i,j;
unsigned int k;
clock_t start, end;
start = clock();
for(k=0;k<9765625;k++)
{
for(i=0;i<n;i++)
{
for(j=i;j<n;j++)
fn_value+=j;
}
}
end= clock();
printf("p3: Time taken %lf - %lf\n", (double) (end-start) / CLOCKS_PER_SEC, (double) fn_value);
}
int main(int argc, char **argv)
{
p1 ();
p2 ();
p3 ();
return 0;
}
Таким образом, double кажется быстрее, чем float. Однако нам нужно увидеть содержимое этого внутреннего цикла, чтобы увидеть, даст ли преобразование типа с плавающей запятой какое-либо ускорение в вашем конкретном случае.
UPDATE
Причина, по которой версия с плавающей запятой медленнее, чем у других, заключается в том, что версия с плавающей запятой постоянно записывает и читает значение в / из памяти. Двойная и рукописная версии никогда не записывают значение в ОЗУ. Почему это так? Основная причина, по которой я могу придумать, состоит в том, чтобы снизить точность значения fn_value между операциями. Внутренне, FPU - 80 бит, тогда как число с плавающей запятой - 32 бита (в этой реализации C). Чтобы сохранить значения в диапазоне с плавающей запятой, компилятор преобразует из 80-битной в 32-битную запись и чтение значения в / из ОЗУ, потому что, насколько я знаю, нет инструкции FPU, чтобы сделать это для одного регистра FPU , Таким образом, для того, чтобы математика оставалась «32-битной» (типа float), она вносит огромные накладные расходы. Компилятор игнорирует различия между 80-битным FPU и 64-битным двойным типом и предполагает, что программист хочет максимально больший тип.