2014-12-06 5 views
1

이것은C 사진 회전 최적화

첫번째 기능은 화상의 화소를 나타내는 이차원 매트릭스 SRC [절전] [어둡게]를 얻어 .. 밖에 뷔페 C 전문가위한 것이며으로 90도 회전 목적지 행렬 dst [dim] [dim]. 두 번째 함수는 동일한 src [dim] [dim]을 사용하고 모든 픽셀 값을 주변 모든 픽셀의 평균 (해당 픽셀을 중심으로하는 최대 3x3 창)으로 대체하여 이미지를 매끄럽게 만듭니다.

내가 시간과주기 위해 계정에서 프로그램을 최적화하는 데 필요한

, 어떻게 다른 내가

void rotate(int dim, pixel *src, pixel *dst,) 
{ 
    int i, j, nj; 
    nj = 0; 

    /* below are the main computations for the implementation of rotate. */ 
    for (j = 0; j < dim; j++) { 
     nj = dim-1-j;    /* Code Motion moved operation outside inner for loop */ 
     for (i = 0; i < dim; i++) { 
      dst[RIDX(nj, i, dim)] = src[RIDX(i, j, dim)]; 
     } 
    } 
} 



/* A struct used to compute averaged pixel value */ 
typedef struct { 
    int red; 
    int green; 
    int blue; 
    int num; 
} pixel_sum; 

/* Compute min and max of two integers, respectively */ 
static int minimum(int a, int b) 
{ return (a < b ? a : b); } 
static int maximum(int a, int b) 
{ return (a > b ? a : b); } 

/* 
* initialize_pixel_sum - Initializes all fields of sum to 0 
*/ 
static void initialize_pixel_sum(pixel_sum *sum) 
{ 
    sum->red = sum->green = sum->blue = 0; 
    sum->num = 0; 
    return; 
} 

/* 
* accumulate_sum - Accumulates field values of p in corresponding 
* fields of sum 
*/ 
static void accumulate_sum(pixel_sum *sum, pixel p) 
{ 
    sum->red += (int) p.red; 
    sum->green += (int) p.green; 
    sum->blue += (int) p.blue; 
    sum->num++; 
    return; 
} 

/* 
* assign_sum_to_pixel - Computes averaged pixel value in current_pixel 
*/ 
static void assign_sum_to_pixel(pixel *current_pixel, pixel_sum sum) 
{ 
    current_pixel->red = (unsigned short) (sum.red/sum.num); 
    current_pixel->green = (unsigned short) (sum.green/sum.num); 
    current_pixel->blue = (unsigned short) (sum.blue/sum.num); 
    return; 
} 

/* 
* avg - Returns averaged pixel value at (i,j) 
*/ 
static pixel avg(int dim, int i, int j, pixel *src) 
{ 
    int ii, jj; 
    pixel_sum sum; 
    pixel current_pixel; 

    initialize_pixel_sum(&sum); 
    for(ii = maximum(i-1, 0); ii <= minimum(i+1, dim-1); ii++) 
     for(jj = maximum(j-1, 0); jj <= minimum(j+1, dim-1); jj++) 
      accumulate_sum(&sum, src[RIDX(ii, jj, dim)]); 

    assign_sum_to_pixel(&current_pixel, sum); 
    return current_pixel; 
} 

void smooth(int dim, pixel *src, pixel *dst) 
{ 
    int i, j; 


    /* below are the main computations for the implementation of the smooth function. */ 

    for (j = 0; j < dim; j++) 
     for (i = 0; i < dim; i++) 
      dst[RIDX(i, j, dim)] = avg(dim, i, j, src); 
} 

? 다음 내가 루프에 대한 내부의 바깥 어두운-1-J를 이동 최적화 할 수있을 것입니다 rotate는 프로그램에서 사용되는 시간과 사이클을 줄이지 만, main 함수에 사용할 수있는 다른 함수가 있습니까?

감사합니다.

+0

그렇다면 제대로 작동하는 코드를 최적화하는 데 도움을 청하고 있습니까? – ryyker

+0

예, stackoverflow에 허용되지 않습니까? 나는 다른 무엇을 최적화 할 수에 붙어있다. 더 숙련 된 C 프로그래머가 그것을 훑어보고 무엇이 명백한지를 지적 할 수 있습니다. – Sean

+0

산술을 포인터와 간단한 추가 연산으로 대체하십시오. 예를 들어, 회전 설정에서 srcPtr과 dstPtr. 소스의 각 행에 대한 Calc 포인터 * dstPtr = * srcPtr ++; dstPtr + = 피치. 이것은 각 픽셀에 대한 곱셈 등을 피합니다. – Chris

답변

1

몇 가지 조치를 취할 수 있습니다. 어떤 컴파일러가 당신을 위해 할 수도 있지만 최선을 다하는 것이 가장 좋습니다. 예를 들어 루프에서 상수 표현식을 이동하면 (한 번 수행 했으므로 할 수있는 더 많은 위치가 있습니다 - 반복마다 조건을 검사하므로 루프 조건을이 방식으로 최적화한다는 점도 잊지 마십시오) Chris가 지적했듯이 전체 배열 인덱싱 대신 증가하는 포인터를 사용합니다. 또한 인라인으로 다시 작성할 수있는 몇 가지 함수 호출을 볼 수 있습니다.

나는 또한 행렬 곱셈에 대한 stackoverflow와 프로세서 캐시를 사용하는 최적화에 대한 기사를 가리키고 싶다. 본질적으로는 먼저 arrrays를 캐시에 맞는 메모리 블록으로 재 배열 한 다음 해당 블록에서 작업을 수행 한 후 다음 블록으로 이동하는 식으로 진행합니다. 당신은 당신의 회전을 위해 아이디어를 재사용 할 수있을 것입니다. Optimizing assembly generated by Microsoft Visual Studio Compiler

1

회전의 경우 작은 이미지 타일로 분해하여 캐시를보다 효율적으로 활용할 수 있습니다.

1), 메인 이중 루프 내부의 전체 동작을 확장이 중간 마이크로 기능을 사용하지 않는 평활

;

2) 완전 누적 해제 및 평균화 (9 개항 만 합쳐짐), 인덱스 하드 코딩.

3) 가장자리를 따라 다른 루프에서 처리 (중간에 모든 9 픽셀을 사용할 수없는 경우). 중간은 최대 최적화가 필요합니다 (특히 (2)).

4) 9로 나누기를 시도하십시오 (테이블 조회로 나누기를 대신 할 수 있습니다).

최고 속도는 수작업으로 벡터화 된 최적화 (SSE/AVX)로 얻지 만 약간의 경험이 필요합니다. 멀티 코어 병렬화도 옵션입니다.

아이디어를 얻으려면 0.5ms (모노 코어, 코어 [email protected]) 미만의 1MB 회색조 이미지에 3x3 평균을 적용 할 수 있습니다. 1Mpixel RGB 이미지의 경우 2ms 정도까지 추정 할 수 있습니다.

1

당신이 그냥 도울 수있는 일의 아이디어입니다 실행중인 프로그램을 제공 할 수 있기 때문에 :

  • 범위 [0256의 값을 가정) 다음 rgbn 값으로 uint8_t을 사용합니다.이것은 int 버전의 메모리의 1/4을 차지하지만 더 많은주기가 필요할 것입니다. 더 많은 지식이 없으면 이것이 더 빠를 지 알 수 없습니다. 메모리의 1/4을 사용하기 때문에 L1-L3 캐시에 더 많은 값을 유지할 가능성이 높습니다.
  • 회전 여부에 관계없이 이웃이 동일하기 때문에 회전하기 전에 평균을 계산하십시오. 나는 이것이 캐싱에 도움이 될 것이라고 의심하지만 다시는 확신 할 수 없다. 그것은 내가 볼 수없는 코드에 달려 있습니다.
  • 외부 루프를 병렬 처리하십시오. 그리드 치수가 쉽고 입출력에 읽기/쓰기 충돌이 없으므로이 작업은 쉽지 않습니다. 확실히 더 많은 사이클이 소요되지만 더 빨라질 수 있습니다.
  • 가장자리를 하드 코드하십시오. 현재 평균에 대한 모든 호출에 대해 최대 및 최소 작업을 수행하고 있지만 내부 점에 대해서는 불필요합니다. 가장자리와 내부 점을 따로 계산하십시오.