Вот небольшой фрагмент кода сборки (я использую синтаксис ассемблера gnu).Почему нажатие двойного на стеке с двумя 32-битными битами может быть намного медленнее, чем нажатие на него с помощью команд float (fldl & fstpl)?
.extern cos
.section .data
pi: .double 3.14
.section .text
.global slowcos
.global fastcos
fastcos:
fldl pi
subl $8, %esp # makes some space for a double on the stack
fstpl 0(%esp) # copy pi on top of the stack
call cos
addl $8, %esp
ret
slowcos:
pushl pi+4 # push the last 4 bytes of pi on top of the stack
pushl pi # push the first 4 bytes of pi on top of the stack
call cos
addl $8, %esp
retx
Можно ссылаться на эти символ легко из C со следующими прототипами:
extern double fastcos();
extern double slowcos();
Они оба возвращают значение «сов (3.14)», но slowcos в два раза медленнее, чем fastcos на Intel 32 -бит архитектуры. Мой вопрос следующий:
Что может объяснить такое большое различие в производительности?
В Linux, вы можете проверить это, скопировав этот код в файл cos.asm вызова и вызова:
as --32 cos.asm -o cos.o
gcc -m32 -O0 cos.o test.c -lm -o test
(вы можете оставить --32/-m32 (должны), если вы? не на 64bits системы) где test.c является следующий исходный C файл:
#include <stdio.h>
#include <time.h>
#define N 40000000
extern double fastcos();
extern double slowcos();
int main() {
int k;
double r;
clock_t t;
t = clock();
for (k = 0; k < N;k ++)
r = fastcos();
printf ("%gs\n",(double) (clock() - t)/CLOCKS_PER_SEC);
printf("fastcos = %g\n", r);
t = clock();
for (k = 0; k < N;k ++)
r = slowcos();
printf ("%gs\n",(double) (clock() - t)/CLOCKS_PER_SEC);
printf("slowcos = %g\n", r);
return 0;
}
на моем компьютере она выводит данные:
1.55687s
fastcos = -0.999999
2.29821s
slowcos = -0.999999
Еще одно замечание. Если вы добавите строку «.global id» в заголовки, замените строки «вызов cos» как fastcos, так и slowcos на «идентификатор вызова» и добавьте следующий «двойной идентификатор (double x) {return x;}» в C. Тогда вы получите:
0.360433s
fastpi = 3.14
0.370393s
slowpi = 3.14
Этот код должен потратить примерно в то же время за пределами внутреннего вызова функции соз (или идентификатор). Поэтому это должно указывать на то, что разница происходит во время выполнения функции cosinus. Но я не понимаю, что может оправдать такую разницу. Нет разницы в выравнивании% esp.
Наконец, я хотел бы сказать, что я наблюдал эти различия в реальном «цифровом» коде, где узким местом часто является вычисление «элементарных математических функций» (например, cos или exp). Кроме того, обе версии создаются компилятором языка программирования высокого уровня. Моя главная задача - понять, что там происходит.
Экспедирование грузов-грузов. – EOF
Хм, довольно уверен, что мой компилятор поднимет вызов cos() из цикла, а затем полностью исключит цикл. Таким образом, вы, вероятно, не используете оптимизированную сборку. Откуда вы знаете, что это не цикл for(), вызвавший замедление? Например, цель ветви не будет выровнена. Попробуйте поменять вызовы на slow/fastcos(). –
@HansPassant: компилятор не разбирает сборку, все, что ей нужно, это прототип функции. Без '__attribute __ ((const))' или что-то подобное, компилятор не может знать, что каждый вызов 'fastcos()' делает то же самое. Однако вы можете быть на что-то с выравниванием функций. – EOF