2010-04-23 2 views
2

У меня есть C/C++ кода, который выглядит следующим образом:Почему оптимизация не происходит?

static int function(double *I) { 
    int n = 0; 
    // more instructions, loops, 
    for (int i; ...; ++i) 
     n += fabs(I[i] > tolerance); 
    return n; 
} 

function(I); // return value is not used. 

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

компилятор, похоже, не имеет значения, я попробовал Intel и gcc. Агрессивная оптимизация, -O3

Благодаря

более полный код (полный код повторение таких блоков):

280   // function registers 
    281   double q0 = 0.0; 
    282   double q1 = 0.0; 
    283   double q2 = 0.0; 
    284 
    285 #if defined (__INTEL_COMPILER) 
    286 #pragma vector aligned 
    287 #endif // alignment attribute 
    288   for (int a = 0; a < int(N); ++a) { 
    289    q0 += Ix(a,1,0)*Iy(a,0,0)*Iz(a,0,0); 
    290    q1 += Ix(a,0,0)*Iy(a,1,0)*Iz(a,0,0); 
    291    q2 += Ix(a,0,0)*Iy(a,0,0)*Iz(a,1,0); 
    292   } 
    293 #endif // not SSE 
    294 
    295   //contraction coefficients 
    296   qK0 += q0*C[k+0]; 
    297   qK1 += q1*C[k+0]; 
    298   qK2 += q2*C[k+0]; 
    299 
    300   Ix += 3*dim2d; 
    301   Iy += 3*dim2d; 
    302   Iz += 3*dim2d; 
    303 
    304  } 
    305  Ix = Ix - 3*dim2d*K; 
    306  Iy = Iy - 3*dim2d*K; 
    307  Iz = Iz - 3*dim2d*K; 
    308 
    309  // normalization, scaling, and storage 
    310  if(normalize) { 
    311   I[0] = scale*NORMALIZE[1]*NORMALIZE[0]*(qK0 + I[0]); 
    312   num += (fabs(I[0]) >= tol); 
    313   I[1] = scale*NORMALIZE[2]*NORMALIZE[0]*(qK1 + I[1]); 
    314   num += (fabs(I[1]) >= tol); 
    315   I[2] = scale*NORMALIZE[3]*NORMALIZE[0]*(qK2 + I[2]); 
    316   num += (fabs(I[2]) >= tol); 
    317  } 
    318  else { 
    319   I[0] = scale*(qK0 + I[0]); 
    320   num += (fabs(I[0]) >= tol); 
    321   I[1] = scale*(qK1 + I[1]); 
    322   num += (fabs(I[1]) >= tol); 
    323   I[2] = scale*(qK2 + I[2]); 
    324   num += (fabs(I[2]) >= tol); 
    325  } 
    326 
    327 
    328  return num; 

мое единственное предположение исключения потенциально с плавающей точкой, которые введены побочные эффекты

+0

Какой компилятор, какие флаги оптимизации? – 2010-04-23 18:52:11

+3

Пожалуйста, положите кусок '...' в свой код. Это может быть важно. –

+0

Можете ли вы опубликовать полную версию, которая показывает проблему? Если я заполню цикл, чтобы выполнить от, скажем, «i = 0» до «i = 10000» и вызывать его из пустого 'main', gcc, похоже, оптимизирует все (за исключением корректировки указателя стека назад и вперед - не знаю, почему это оставляет это :)) –

ответ

7

Код действительно использует n, сначала, когда он инициализирует его 0, а затем внутри цикла в левой части функции с возможными побочными эффектами (fabs).

ли вы или нет на самом деле использовать возврат функции не имеет значения, n сама является используется.

Обновление: я пробовал этот код в MSVC10 и оптимизировал всю функцию. Дайте мне полный пример, который я мог бы попробовать.

#include <iostream> 
#include <math.h> 

const int tolerance=10; 

static int function(double *I) { 
    int n = 0; 
    // more instructions, loops, 
    for (int i=0; i<5; ++i) 
     n += fabs((double)(I[i] > tolerance)); 
    return n; 
} 


int main() 
{ 
    double I[]={1,2,3,4,5}; 

    function(I); // return value is not use 
} 
+0

Быстрая и точка ответа. Beat to the punch here :) –

+0

yes, но n используется только по отношению к себе, он не используется как правое значение в любом из выражений. Я ожидаю, что компилятор узнает, что – Anycorn

+1

После вставки код выглядит как 'int n = 0; для (...) n + = ...; '. После этого цикла 'n' больше не используется. Это может привести к возможным побочным эффектам 'fabs' без использования хранилища для' n', если я не пропущу что-то. Он мог даже знать о 'fabs' (см.' -fno-builtin') и оптимизировать весь цикл. Но, видимо, этого не происходит. –

2

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

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

+0

Учитывая недавно опубликованный код, это, вероятно, правильный ответ. Компиляторы не являются волшебными машинами. – Torlack

+2

В общем случае это не так уж редко. Функция может генерировать два возвращаемых значения, только один из которых вам действительно нужен. Или фактическая работа функции может быть полностью ортогональной возвращаемому значению; функция, которая строит массив, например, и возвращает количество значений в массиве. Если вам не все равно количество значений в массиве, пока массив будет создан, вы можете игнорировать возвращаемое значение. –

+0

Я согласен, что вполне нормально игнорировать возвращаемые значения. Но в таких случаях возвращаемое значение обычно тривиально, как в примере вашего массива. Это означает, что вы получите небольшую экономию, оптимизируя ее. Однако код для этой оптимизации будет сложным. –

1

Я не могу сказать наверняка, что это будет иметь эффект, но вы можете посмотреть в атрибутах GCC pure и const (http://gcc.gnu.org/onlinedocs/gcc/Function-Attributes.html). Он в основном сообщает компилятору, что функция работает только на своем входе и не имеет побочных эффектов.

С учетом этой дополнительной информации он может определить, что вызов не нужен.

+0

они, похоже, добавили больше параметров оптимизации, которые могут быть полезны для me.thanks – Anycorn

+0

Я бы предположил, что вы хотите поместить const на входную функцию (const double * I) как для правильности, так и, возможно, это поможет оптимизировать компилятор, не знаю пока вы не попробуете. –

+0

@dweltch: пока вы правы в предоставлении этого совета. Я действительно имел в виду атрибут GCC ** ** с именем 'const'. См. Ссылку, которую я опубликовал для получения подробной информации. –

0

Несмотря на аргументы, которые у меня были в других потоках, где все компиляторы идеальны и никогда не пропускают оптимизацию. Компиляторы не идеальны и не часто ловят оптимизацию.

Fun из них, как это:

 
int fun (int a) 
{ 
    switch(a&3) 
    { 
     case 0: return(a+4); 
     case 1: return(a+2); 
     case 2: return(a); 
     case 3: return(0); 
    } 
    return(1); 
} 

Долгое время, если вы оставили, что возвращение в конце из вы получите сообщение об ошибке, что функция определяется тип возвращаемого значения, но не возвращает значение. Некоторые компиляторы будут жаловаться на return() в конце функции и жаловаться без нее.

Из того, что я могу сказать от gcc vs say llvm, gcc оптимизирует внутри функции внутри файла, где llvm оптимизирует все, что он подает. И вы можете присоединиться к байт-коду для всего проекта в один файл и оптимизировать все это одним выстрелом. В настоящий момент gcc-выход превосходит llvm на десяток процентов или более процентов, что интересно. это займет время.

Возможно, в вашем случае вы используете два входа, которые не объявлены как static (const), поэтому результат n может измениться. Если он оптимизирован по принципу работы, он не сможет его уменьшить. Поэтому я предполагаю, что это оптимизация для каждой функции, и вызывающая функция не знает, что влияет на динамический ввод, который я получил в системе, даже если возвращаемое значение не использовалось, все равно нужно вычислить функцию (I), чтобы решить все, что зависит от I. Я предполагаю, что это не бесконечный цикл, ... означает, что наложено ограничение? Если не здесь снова динамическое не статическое, функция (I) может быть завершающей бесконечной функцией цикла или она может быть там, ожидая, что подпрограмма обслуживания прерывания изменит I и вытолкнет ее из бесконечного цикла.

Смежные вопросы