2017-11-17 3 views
1

에서 루프 I가 다음과 같은 구조 :포인터 C 프로그램

또한
struct Matrix 
{ 
    int  numOfRows; 
    int  numOfColumns; 
    double* values; 
    int* permutationVector; 
} 

나는 다음과 같은 기능이 있습니다 궁금

void SetRowToZero(Matrix* m, int row) 
{ 
    int rowBegin = row*(m->numOfColumns); 
    for (int c = 0; c < (m->numOfColumns); c++) 
    { 
     m->values[rowBegin + c] = 0; 
    } 
} 

작동 c < (m->numOfColumns) 중에 성능 저하가 있습니까?

void SetRowToZero(Matrix* m, int row) 
{ 
    // Unpacking structure 
    int numOfColumns = m->numOfColumns; 
    double* values = m->values; 

    int rowBegin = row*(m->numOfColumns); 

    for (int c = 0; c < numOfColumns; c++) 
    { 
     values[rowBegin + c] = 0; 
    } 
} 

그리고 일반적으로

, 난 심지어 작은 규모의 performace에 대해 관심을 가져야 :이 같은 기능을 작성하는 경우 어떤 차이가 있나요?

+0

왜 C++ 태그를 사용합니까? –

답변

4

컴파일러 최적화를 통해 두 코드 모두 해당 값에 대한 레지스터를 사용하도록 최적화됩니다. x86_64에서이 작은 프로그램을 테스트하고 gcc 5로 컴파일했습니다.

gcc -O4 main.c -o test1.out 

를하고 난 컴파일

SetRowToZero2(&matrix, 1); 

SetRowToZero1(&matrix, 1); 

변경 : 레벨 4 4 최적화

#include <stdlib.h> 

struct Matrix 
{ 
    int  numOfRows; 
    int  numOfColumns; 
    double* values; 
    int* permutationVector; 
}; 

void SetRowToZero1(struct Matrix* m, int row) 
{ 
    int rowBegin = row*(m->numOfColumns); 
    for (int c = 0; c < (m->numOfColumns); c++) 
    { 
     m->values[rowBegin + c] = 0; 
    } 
} 


void SetRowToZero2(struct Matrix* m, int row) 
{ 
    // Unpacking structure 
    int numOfColumns = m->numOfColumns; 
    double* values = m->values; 

    int rowBegin = row*(m->numOfColumns); 

    for (int c = 0; c < numOfColumns; c++) 
    { 
     values[rowBegin + c] = 0; 
    } 
} 

int main() { 
    struct Matrix matrix = {5,1000000, malloc(5 * 1000000 * sizeof(double)), NULL}; 
    SetRowToZero1(&matrix, 1); 
} 

그것을 컴파일

01,235 16,
gcc -O4 main.c -o test2.out 

다음 : $ md5sum이 test1.out test2.out

504fb75e97173a6864750f5feb7cea58 test12.out

504fb75e97173a6864750f5feb7cea58 test1.out

그래서 당신이 확실하게 말할 수있는 구현이 차이를하지 않습니다 :

2

두 코드간에 차이점은 없습니다. 그들은 중간 변수를 사용하는 것과 동일한 것을합니다.

가독성 관점에서 임시 파일을 사용하는 것이 합당한 경우 사용하고 그렇지 않으면 사용하지 마십시오.

3

Maxim : 가독성이 성능을 향상시킵니다.

다른 말로하면, 컴퓨터는 싸지 만 프로그래머는 그렇지 않습니다.


루프 조건은 단순한 멤버 액세스이므로이 경우에는 차이가 없습니다.

조건부 루프가 함수라면 다른 문제 일 수 있습니다. 그러나이 경우에 당신은 뒤로 루프를 실행할 수 있습니다 : cunsigned 유형 인 경우

for (int c = <expensive function> - 1 c >= 0; --c) 
{ 
    m->values[rowBegin + c] = 0; 
} 

추가 돌보는.

4

최적의 컴파일러는 첫 번째 코드 단편을 두 번째 코드 단편으로 변환하고 레지스터에 포인터를 저장하거나 인덱스 c을 최적화하거나 다음과 같은 주소 지정 모드를 사용해야합니다. 하드웨어에서 offset+index을 계산합니다.

참고 : 포인터 계산을 선호하면 인덱스를 사용하지 않고 루프를 다시 작성할 수 있습니다. 이 코드의 성능은 코드의 원래 두 블록의 그것과 유사하다, 그러나 레지스터에 저장 포인터와 어셈블리 코드의 간단한 블록에 거의 직접 번역 :

void SetRowToZero(Matrix* m, int row) { 
    double *rowPtr = m->values + (row*(m->numOfColumns)); 
    double *pastEndPtr = rowPtr + m->numOfColumns; 
    while (rowPtr != pastEndPtr) { 
     *rowPtr++ = 0; 
    } 
} 

또한 세 가지 구현하지 참고 thread-safe : m->numOfColumns 또는 m->values의 값이 루프의 중간에서 다른 스레드에 의해 변경되면 정의되지 않을 가능성이 높고 예상치 못한 결과가 발생할 수 있습니다.

+0

다음과 같은 경우 (이론적으로) :m1-> m2-> m3-> m4 -> ...-> mn)? 그리고 갑자기 다른 곳에서 m3이 바뀌 었습니까? – Eugene