2010-10-26 2 views
2

Две части два мой вопрос. Что является более эффективным/быстрее:длинные строки целочисленной арифметики

int a,b,c,d,e,f; 
int a1,b1,c1,d1,e1,f1; 
int SumValue=0; // oops forgot zero 
// ... define all values 
SumValue=a*a1+b*b1+c*c1+d*d1+e*e1*f*f1; 

или

Sumvalue+=a*a1+b*b1+c*c1; 
Sumvalue+=d*d1+e*e1*f*f1; 

Я предполагаю, что первый из них. Мой второй вопрос - почему.

Я предполагаю, что третий вопрос заключается в том, что в любом случае необходимо разбить операцию добавления (помимо ограничений компилятора на количество продолжений строк и т. Д.).

Редактировать

единственный раз, когда я хотел бы видеть замедление, когда то вся операция арифметика не может поместиться в кэше? Я думаю, что это невозможно - компилятор, вероятно, злится на два ряда строк, прежде чем это произойдет. Может быть, мне придется играть завтра и посмотреть.

+0

Его арифметика, с 'e' :-) – Arun

+1

Положите их в действительно большую петлю, и пора их. Тогда вы узнаете, какой из них быстрее. – abelenky

+0

@ArunSaha, зачем комментировать, когда вы можете редактировать? :-) – ergosys

ответ

5

Вы измерили это? Оптимизированный машинный код для обоих подходов, вероятно, будет очень похожим, если не совпадать.

EDIT: Я просто проверил это, результаты, что я ожидал:

$ gcc -O2 -S math1.c # your first approach 
$ gcc -O2 -S math2.c # your second approach 
$ diff -u math1.s math2.s 

--- math1.s 2010-10-26 19:35:06.487021094 +0200 
+++ math2.s 2010-10-26 19:35:08.918020954 +0200 
@@ -1,4 +1,4 @@ 
- .file "math1.c" 
+ .file "math2.c" 
    .section .rodata.str1.1,"aMS",@progbits,1 
.LC0: 
    .string "%d\n" 

Вот и все. Идентичный машинный код.

+2

Да, но машинный код, который вы создали, вероятно, даже не выполняет арифметику, по крайней мере, если вы скопировали фрагмент из OP. Если вы используете флаги оптимизации, это, вероятно, оптимизирует арифметику, поскольку вы фактически ничего не делаете с 'SumValue'. Когда я смотрю на сборку, сгенерированную gcc, она даже не выполняет арифметику, если я устанавливаю флаги оптимизации. Единственное в разделе '.main' - инструкция' ret'. –

+0

спасибо - должен был разобраться сам, я не думал:) – Marm0t

+0

@Charles, я убедился, что 'SumValue' использовался, передавая его' printf ("% d \ n") ', и результаты одинаковы , –

0

Скорее всего, они будут преобразованы в одно и то же количество машинных инструкций, чтобы они занимали одинаковый период времени.

1

Вот почему это помогает быть знакомым с ассемблером. В обоих случаях будут созданы инструкции по сборке, которые загружают пары операндов в регистры и выполняют сложение/умножение и сохраняют результат в регистре. Инструкции по сохранению конечного результата в адресе памяти, представленном SumValue, также могут быть сгенерированы в зависимости от того, как вы используете SumValue.

Короче говоря, обе конструкции, вероятно, будут работать одинаково, особенно с флагами оптимизации. И даже если они не выполняют, то на какой-то платформе они выполняются одинаково, для любого подхода нет ничего, что могло бы помочь объяснить, почему на уровне C++. В лучшем случае вы сможете понять причину, по которой один лучше, чем другой, посмотрев, как ваш компилятор переводит конструкции C++ в инструкции по сборке.

Я думаю, третий вопрос, в любой точке было бы необходимо разорвать вверх операцию сложения (кроме того ограничений компилятора на номере строки продолжений и т.д ...).

Это действительно не , чтобы разделить операцию добавления. Но это может помочь в удобочитаемости.

2

Существует не произвольное ограничение количества операций, которые вы можете комбинировать на одной линии ... практически, компилятор примет любое число, которое вы хотите бросить на него. Составление компиляторами операций происходит задолго до того, как новые строки зацикливаются - к этому времени он имеет дело с лексическими символами и правилами грамматики, а затем с абстрактным синтаксическим деревом. Если ваш компилятор очень плохо написан, оба оператора будут одинаково хорошо работать для данных int.

Обратите внимание, что в результате = a * b + c * d + e * f и т. Д. Компилятор не имеет точек последовательности и знает приоритет, поэтому имеет полную свободу для оценки и комбинирования подвыражений параллельно (с учетом совместимого оборудования) , В результате + = a * b; результат + = c * d; подход, вы вставляете точки последовательности, чтобы компилятор попросил выполнить одно выражение перед другим, но свободно - и должен - понять, что результат не используется в другом месте между приращениями, поэтому его можно оптимизировать, как в первом случае ,

В целом: лучший совет, который я могу дать для таких запросов производительности, - это 1) не беспокойтесь о том, что это практическая проблема, если ваша программа работает слишком медленно, а затем профиль, чтобы узнать, где 2) если любопытное или профилирование указывает на проблемы, затем попробуйте оба/все подходы, которые вы можете придумать и измерить реальную производительность.

Кроме того: + = может быть более эффективным иногда, например. для конкатенации существующей строки, так как + на таких объектах может включать создание временных и большего объема памяти - шаблонные выражения работают вокруг этой проблемы, но редко используются как очень сложные для реализации и медленнее компиляции.

+0

+1 для не беспокоиться об этом и профилировать свою программу – rve