Git Product home page Git Product logo

code_optimization's Introduction

Code_Optimization Test(C++ Language)

Environment

  • Processor: Intel(R) Core(TM) i5-9600KF CPU @ 3.70GHz 3.70GHz
    • 6 Cores, 6 Threads
  • RAM : 16GB
  • x64 기반 프로세서, Windows10
  • Graphic Card: NVIDIA GeForce GTX1660Ti 6GB
    • 1536 Cuda Cores, 24 SM(Streaming Multiprocessor)s

순서

Code Optimization 기법

  • Spatial Locality

    • 개념
      • 최근에 접근한 데이터로부터 가까운 곳에 있는 데이터는 다시 이용될 확률이 높아, 특정 데이터를 Caching할 때 근처의 데이터들 역시 Caching하게 됨
    • 개요
      • 행렬의 곱셈(a*b)에서, 일반적으로 코드를 짜게되면 b 행렬에 접근할 때 연속적인 메모리영역을 접근하는 것이 아니므로 상대적으로 cache miss가 많이 발생하게 됨
      • 이 때, b 행렬을 transpose한 행렬을 사용하게 되면 연속적으로 메모리영역을 접근할 수 있어 cache hit가 많이 발생하게됨
      • 따라서 속도가 상대적으로 빠를 것임
    • 코드
      • Locality를 고려한 경우
        for (int i = 0; i < N; i++)
        {
          for (int j = 0; j < N; j++)
          {
            for (int k = 0; k < N; k++)
            {
              result[i * N + j] += a[i * N + k] * b[k * N + j];
              //매 iteration마다 b를 접근할 때에, 연속적인 memory 영역을 접근하는 것이 아닌, 띄엄띄엄 접근하게 됨 
              //spatial locality를 고려하지 않음.
            }
          }
        }
        
      • Locality를 고려하지 않은 경우
        for (int i = 0; i < N; i++)
        {
          for (int j = 0; j < N; j++)
          {
            for (int k = 0; k < N; k++)
            {
              result[i * N + j] += a[i * N + k] * transpose_b[j * N + k];
              //transpose_b를 사용하여, 매 iteration마다 b를 접근할 때, 연속적인 memory 영역을 접근할 수 있음
              //spatial locality를 고려
            }
          }
        }
        
    • 결과
      image
      • Locality를 고려한 코드가 더 빠른 것을 확인할 수 있음
  • Common Subexpression Elimination

    • 개념
      • 같은 코드를 중복으로 실행하지 않고, 해당 코드를 한번 실행한 값을 저장하고 해당 값을 사용함으로써 최적화하는 것
    • 개요
      • pow(b, c) 코드는 b의 c제곱을 실행하는 함수임
      • 코드 전반에 걸쳐 공통적으로 사용되는 pow(b, c)를 common이라는 변수에 저장하여 이를 사용
      • 따라서 속도가 빠를 것임
    • 코드
      • Common Subexpression Elimination을 하지 않은 경우
        void without_cse(float a, float b, float c, float* result)
        {
            for (int i = 0; i < N * N; i++)
            {
                //result를 구하는 연산에 pow(b, c)가 여러번 들어감
                *result += pow(b, c) + pow(b, c) * a;
                *result -= pow(b, c) - pow(b, c) + b;
                b++; c++;
            }
        }
        
      • Common Subexpression Elimination을 수행한 경우
        void with_cse(float a, float b, float c, float* result)
        {
            for (int i = 0; i < N * N; i++)
            {
                //공통적으로 들어가는 pow(b,c)를 common이라는 변수에 저장해주고
                //이를 result값 계산에 사용
                double common = pow(b, c);
                *result += common + common * a;
                *result -= common - common + b;
                b++; c++;
            }
        }
        
    • 결과
      image
      • Common Subexpression Elimination을 수행한 코드가 더 빠른 것을 확인할 수 있음
  • Loop Unrolling

    • 개념
      • Loop안의 내용을 펼쳐서 비교와 관련된 연산을 줄여 코드를 최적화하는 것
    • 개요
      • Loop에서는 지정된 범위에 도달했는지의 여부에 대해 검사하기 위해, 값을 비교하고 증가 및 감소 시키는 작업이 진행됨
      • Loop를 펼쳐서 이러한 과정을 줄이게 됨
      • 따라서 속도가 빠를 것임
    • 코드
      • Loop Unrolling를 하지 않은 코드
      void without_loop_unrolling(float* a, float* result)
      {
          for (int i = 0; i < pow(N, 2); i++)//값을 비교하는 작업에서의 시간을 확실히 비교하기 위해 N*N을 사용한 것이 아닌 pow(N, 2)사용
          {
         	*result += a[i];
          }
      }
      
      • Loop Unrolling를 한 코드의 경우
      void with_loop_unrolling(float* a, float* result)
      {
          for (int i = 0; i < pow(N, 2); i+=4)//값을 비교하는 작업에서의 시간을 확실히 비교하기 위해 N*N을 사용한 것이 아닌 pow(N, 2)사용
          {
      	*result += a[i];
      	*result += a[i + 1];
      	*result += a[i + 2];
      	*result += a[i + 3];
          }
      }
      
    • 결과
      image
      • Loop Unrolling을 수행한 코드가 더 빠른 것을 확인할 수 있음
  • Function Inlining

    • 개념
      • 작은 크기의 함수의 경우에는, 함수로 따로 만들지 않고 로직을 그대로 코드에 삽입하여 최적화
    • 개요
      • 함수를 호출하게 되면, return adress를 스택에 저장하고 해당 함수로 jump하게 됨
      • 또한 함수에서 사용되는 일반적인 register를 스택에 백업하고, register 값을 복원하는 과정을 거치면서 시간이 소요되게 된다.
      • 작은 크기의 함수의 경우, 함수로 따로 만들지 않고 로직을 그대로 코드에 삽입하여 시간을 줄이게 됨
      • 따라서 속도가 빠를 것임
    • 코드
      • Function Inlining을 하지 않은 코드의 경우
        float add_func(float a, float b)
        {
              float ret = a + b;
              return ret;
        }
        void without_function_inlining(float* a, float* b, float* result)
        {
              for (int i = 0; i < pow(N, 2); i++)
              {
                        *result += add_func(a[i], b[i]);
              }
        }
        
      • Function Inlining을 한 코드의 경우
        void with_function_inlining(float* a, float* b, float* result)
        {
              for (int i = 0; i < pow(N, 2); i++)
              {
                       *result += a[i] + b[i];
              }
        }
        
    • 결과
      image
      • Function Inlining을 수행한 코드가 더 빠른 것을 확인할 수 있음
  • Code Motion

    • 개념
      • 기존의 코드를 새로운 위치로 이동함으로써 최적화
    • 개요
      • a + pow(b, c)는 반복문 안에서 지속적인 시간을 소요하지만 따로 값의 변화가 일어나지는 않음
      • a + pow(b, c)를 반복문 바깥으로 옮김으로써 이러한 지속적인 시간소요를 줄이게 됨
      • 따라서 속도가 빠를 것임
    • 코드
      • Code Motion을 하지 않은 코드의 경우
        void without_code_motion(float a, float b, float c, float* result)
        {
             for (int i = 0; i < N * N; i++)
             {
                //result를 구할 때에 a+pow(b,c)가 들어가게 되는데 이 값은 루프를 돌면서 변하지 않음
                *result += (i % 5) + a + pow(b, c);
             }
        }
        
      • Code Motion을 한 코드의 경우
        void with_code_motion(float a, float b, float c, float* result)
        {
             //따라서 code_motion에서는 a + pow(b,c)를 for문 밖으로 옮겨줌
             float temp = a + pow(b, c);
             for (int i = 0; i < N * N; i++)
             {
                     *result += (i % 5) + temp;
             }
        }     
        
    • 결과
      image
      • Code Motion을 수행한 코드가 더 빠른 것을 확인할 수 있음
  • Instruction Scheduling


CPU

[Thread 개수 및 메모리 접근 방식에 따른 성능 비교]

1부터 N-1까지를 더하는 코드를 각 경우에 맞게 작성하여 시간을 측정
  • Global Variable만을 활용하는 경우
    • 개요
      • 모든 thread에서 공유하는 global_sum이라는 공유변수를 이용
      • 각 thread에서는 자신이 맡은 범위까지의 덧셈을 바로바로 global_sum에 반영
      • 해당 과정에서 각 thread가 global_sum에 접근할 때는 올바른 값의 도출을 위해 semaphore로 감싸주게 됨
    • 코드
      //각 thread가 실행할 함수
      void sum_global_variable(void* vargp)
      {
          //num_per_thread의 값은 read만 하기 때문에 따로 semaphore로 감싸줄 필요가 없음
          long id = (*(long*)vargp);
          long start = id * num_per_thread;
          long end = start + num_per_thread;
          if (end > N)
              end = N;
      
          long i;
          for (i = start; i < end; i++)
          {
              lock_mutex.lock();
              global_sum += i;//global_sum이라는 공유데이터를 변경하는 코드영역, 즉 크리티컬섹션을 semaphore로 감싸줌
              lock_mutex.unlock();
          }
      }
      
    • 결과
      image
      • thread의 수를 늘림에도 불구하고, 오히려 느려지는 것을 확인할 수 있음
      • 하나의 공유 데이터에 접근하기 때문에, thread를 사용하더라도 semaphore를 통해 순차적으로 접근하는 것과 같음
      • 또한 매번 더할 때마다 semaphore에 의한 overhead가 발생하기 때문임
  • Global Array를 사용해 Mutex Overhead를 줄인 경우
    • 개요
      • 메인 thread에서는 global_sum이라는 변수를 이용
      • 모든 thread에서는 global_array라는 공유변수 배열을 이용
      • 각 thread는 자신이 맡은 범위까지의 덧셈을 수행하여 자신이 담당하는 global_array의 해당 index에 저장
      • 마지막으로 메인 thread가 이러한 global_array의 값들을 취합하여 global_sum에 최종 값을 저장
    • 코드
      //메인 thread에서 최종적으로 global_array의 내용들을 취합해서 sum을 하는 과정
      void total_sum(int thread_num)
      {
          long i;
          for (i = 0; i < thread_num; i++)
              global_sum += global_array[i];
      }
      //각 thread가 실행할 함수
      void sum_global_array(void* vargp)
      {
          //num_per_thread의 값은 read만 하기 때문에 따로 semaphore로 감싸줄 필요가 없음
          long id = (*(long*)vargp);
          long start = id * num_per_thread;
          long end = start + num_per_thread;
          if (end > N)
              end = N;
      
          long i;
          for (i = start; i < end; i++)
          {
              global_array[id] += i;//global_array의 자신이 담당하는 index에, 자신이 맡은 영역의 sum을 저장해줌
          }
      }
      
    • 결과
      image
      • Thread의 수가 적절히 많아질 수록 속도가 빨라지는 경향을 확인할 수 있음
        • 따라서 프로세서의 Core 수 및 작업에 따라 적절히 Thread의 수를 정해주는 것이 필요
      • Global Variable에 비해 속도가 빠른 것을 확인 할 수 있음
        • 각 Thread는 Global Array의 자신의 Index에 해당하는 부분만 접근하기 때문에 Semaphore를 사용하지 않음
        • 이를 통해 Semaphore에 따른 Overhead가 줄어들었기 때문에 속도가 빨라짐
  • Thread내의 Local Variable을 사용해 Register를 사용하도록 한 경우
    • 개요
      • 메인 thread에서는 global_sum이라는 변수를 이용
      • 모든 thread에서는 global_array라는 공유변수 배열을 이용
      • 모든 thread에서는 local_sum이라는 지역변수를 이용
      • 각 thread는 자신이 맡은 범위까지의 덧셈의 과정을 자신의 지역변수인 local_sum을 이용해 수행
      • 각 thread는 최종적으로 local_sum의 값을 global_array의 자신이 담당하는 index의 위치에 대입
      • 마지막으로 메인 thread가 이러한 global_array의 값들을 취합하여 global_sum에 최종 값을 저장
    • 코드
      //메인 thread에서 최종적으로 global_array의 내용들을 취합해서 sum을 하는 과정
      void total_sum(int thread_num)
      {
          long i;
          for (i = 0; i < thread_num; i++)
              global_sum += global_array[i];
      }
      //각 thread가 실행할 함수
      void sum_local_register(void* vargp)
      {
          long id = (*(long*)vargp);
          long start = id * num_per_thread;
          long end = start + num_per_thread;
          //thread에 local변수를 만들어서 이를 이용하도록 하여
          //메인 메모리에 대한 접근을 줄이고
          //(컴파일러가 최적화 하면서 local변수는 register를 이용하도록 하기 때문에)
          //메인 메모리 영역에 있는 global_array는 
          //한번만 접근하게 해줌으로써 속도를 높임
          long long local_sum = 0;
          if (end > N)
              end = N;
      
          long i;
          for (i = start; i < end; i++)
          {
               local_sum += i;//local_sum을 이용해 자신이 맡은 영역의 sum을 계산해주도록 함
          }
      
          //global array는 각 thread마다 한번만 접근함
          global_array[id] = local_sum;
      }
      
    • 결과
      image
      • Thread의 수가 적절히 많아질 수록 속도가 빨라지는 경향을 확인할 수 있음
        • 따라서 프로세서의 Core 수 및 작업에 따라 적절히 Thread의 수를 정해주는 것이 필요
      • Global Array에 비해 속도가 빠른 것을 확인 할 수 있음
        • 각 thread에서 계산시 지역변수(local_sum)을 이용하게 되면 Compiler에 의해 Register를 사용하도록 하기 때문에 Memory Reference가 줄어들게 되고 속도가 더 빨라짐
  • Thread의 수 정리
    • CPU를 많이 사용하는 작업의 경우
      • N개의 CPU가 있으면 N+1개의 Thread를 두는 것이 좋음
    • I/O 및 다른 블로킹이 많은 작업의 경우
      • 작은 수의 Thread를 사용하게 되면 모든 Thread가 I/O작업을 기다리게 되는 경우가 생길 수 있게 되고 이런 경우에는 CPU는 일을 안하는 상태가 됨
      • 따라서 많은 수의 Thread를 두는 것이 좋음
    • 공식
      Thread의 수 = CPU의 개수 * CPU활용도[0~1] * (1 + 작업시간 대비 대기시간의 비율)
      

CPU vs GPU

두 Array의 합을 구하는 코드를 통해 테스트
  • 사전지식 및 예측(GPU)
    • 1660Ti는 1536개의 CUDA 코어(PE)와, 24개의 SM(Streaming Multiprocessor)로 구성
      • 하나의 SM에는 1536/24 = 64개의 CUDA 코어가 포함되어 있음
    • 동일한 연산이 서로 다른 데이터에 대해 반복되고, 각 연산이 서로 독립적인 경우, GPU를 사용하는 것이 유리
      • SIMD방식: 하나의 연산을 동일한 형태로 존재하는 서로 다른 데이터 각각에 대해 병렬적으로 수행시키는 방법
    • 특정 데이터에 대한 연산을 하나의 Task(thread)라고 하고, Task는 하나의 코어에서 작업을 처리하게 됨
    • 전체 Task들은 각각 동일한 개수의 Task를 가지도록 Thread Block(Task들의 그룹)으로 나뉘고, 같은 Thread Block내의 모든 Task들은 모두 동일한 SM에서 병렬적으로 처리가 됨
      • 1660Ti의 경우, 하나의 SM에 64개의 코어를 가지고 있으므로 Thread Block의 크기를 64 이상으로 하는 것이 성능 향상에 좋을 것이라고 예측해볼 수 있음
        • 64보다 작은 경우에는, 하나의 SM 내에서 놀게되는 코어가 생길 것이기 때문에
      • 실질적으로는, Thread Block이 Warp라는 단위로 또 나뉘어, SM에서 Warp 단위로 병렬적으로 처리가 되고 Warp들 사이의 순서는 알수 없음
    • CPU vs GPU
      • CPU : 순차적으로 데이터를 처리하는 방식에 특화된 구조, GPU에 비해 한 개의 코어가 가지고 있는 ALU의 개수가 적음
      • GPU : 여러 명령을 동시에 처리하는 병렬 처리에 특화된 구조, CPU에 비해 한 개의 코어가 가지고 있는 ALU의 개수가 많음
      • 따라서, CPU에 비해 많은 코어를 가지고 있고, 병렬 처리에 특화되어 있는 GPU가 두 Array의 합과 같은 계산에서 빠른 속도를 보일 것이라고 예측해볼 수 있음
  • GPU계산
    • 2^28개의 원소를 가지는 두 Array의 합 계산
    • 개요
      • 각 Task는 자신이 맡은 Index의 Array의 합 수행
      • Thread Block의 크기(하나의 Thread Block 안의 Thread의 개수)를 조절해가면서 테스트
    • 코드
      int BLOCK_SIZE =64;//BLOCK당 THREAD의 개수 설정
      //하나의 Task가 수행하는 함수
      __global__ void SumArraysKernel(Array A, Array B, Array C) {
          //계산이 수행어야 하는 task의 id(Array 내에서의 index라고 보면 됨)를 구해줌
          int row = blockDim.y * blockIdx.y + threadIdx.y;
          int col = blockDim.x * blockIdx.x + threadIdx.x;
          int id = gridDim.x * blockDim.x * row + col;
          //Array A와 Array B의 합을 c에 저장
          C.elements[id] = A.elements[id] + B.elements[id];
      }
      
    • 결과
      • Block Size 8
        image
      • Block Size 16
        image
      • Block Size 32
        image
      • Block Size 64
        image
      • Block Size 128
        image
      • Block Size 256
        image
      • Block Size 512
        image
      • Block Size 1024
        image
      • Block Size에 따라 속도가 달라지는 것을 확인할 수 있음
        • 따라서 GPU SM안의 Core를 고려하여 적절히 Block의 크기를 정해주는 것이 필요
  • CPU
    • 2^27개의 원소를 가지는 두 Array의 합 계산
      • Memory 문제로 인해서 2^28개로 테스트 하지 못함
    • 개요
      • 각 Thread는 자신이 맡은 범위에 해당하는 Array의 합 계산
      • 이전의 CPU Test에서 가장 빠른 속도를 보였던, 32를 Thread의 개수로 설정하여 테스트
    • 코드
      #define MAX_THREADS 32//Thread의 개수 설정
      void sum_array(void* vargp)//자신이 맡은 범위에 대한 계산 수행
      {
          long id = (*(long*)vargp);
          long start = id * num_per_thread;
          long end = start + num_per_thread;
      
          if (end > MAX_N_ELEMENTS)
              end = MAX_N_ELEMENTS;
          long i;
          for (i = start; i < end; i++)
          {
               C_RESULT[i] = A[i] + B[i];//array에서 자신이 맡은 영역의 array의 합을 구해주도록 함
          }
      }
      
    • 결과
      image
  • 결과 정리
    • CPU가 처리해야 하는 연산 숫자가 GPU에 비해 2배 적음에도 불구하고, Block Size에 관계없이 GPU가 더 빠른 것을 확인할 수 있음
    • Block Size에 따라 GPU의 병렬 처리 속도가 다른 것을 확인할 수 있으므로, Block Size를 잘 고려하는 것이 중요함

GPU without Shared Memory vs GPU with Shared Memory

  • 사전지식(GPU)
    • CUDA 메모리 계층 구조
      image
      • Register
        • 각 PE는 Register File을 가지고 있음
        • On Chip Memory로 가장 속도가 빠름
        • PE에 배정된 Thread에서만 접근 가능하고 LifeTime 역시 해당 Thread와 같음
      • Local Memory
        • PE의 Register가 부족한 경우 Local Memory를 이용하게 됨
        • Off Chip Memory로 속도가 느림
        • PE에 배정된 Thread에서만 접근 가능하고 LifeTime 역시 해당 Thread와 같음
      • Shared Memory
        • 각 SM은 L1 data cache/Shared Memory를 가지고 있음
        • On Chip Memory로 레지스터보다는 느리지만 속도가 빠름
        • SM에 배정되는 Thread Block의 모든 Thread들에서 Shared Memory를 접근 가능하며 공유
        • LifeTime 역시 해당 Thread Block과 같음
      • Global Memory
        • Off Chip Memory로 속도가 느림(수백 사이클)
        • Host Application안의 모든 Thread들이 접근 가능하고, LifeTime은 Host Application과 같음
      • Constant Memory
        • Off Chip Memory로 상수 값을 저장하기 위한 메모리
        • Host Application안의 모든 Thread들이 접근 가능하고, LifeTime은 Host Application과 같음
        • 같은 Warp안의 모든 Thread가 같은 위치를 접근하는 경우 빠름
        • Constant Cache가 있기 때문에 Global Memory보다는 빠름
      • Texture Memory
        • Off Chip Memory로 Texture 이미지를 저장하기 위한 메모리
        • Host Application안의 모든 Thread들이 접근 가능하고, LifeTime은 Host Application과 같음
        • 같은 Warp안의 모든 Thread가 물리적으로 인접한 위치를 접근하는 경우 빠름
        • Texture Cache가 있기 때문에 Global Memory보다 빠름
    • Global Memory 부가설명
      • Global Memory 는 속도가 느림
      • 병렬적 수행에 있어, Warp내의 32개의 Thread들로부터 32개의 메모리 접근 시도가 일어나게 됨
      • 이러한 32번의 접근을 순차적으로 하는 것이 아닌, 메모리 접근시도를 최대한 합쳐서(Memory Coalescing), 적은횟수의 메모리 접근을 하고자 함
      • Memory Coalescing을 하는 경우, 각 메모리 접근이 지역적으로 멀리 떨어져 있는 경우 여러 번의 메모리 접근이 필요하기 때문에 효율이 떨어지게 됨
      • 따라서 프로그래머는 이러한 Memory 접근을 최적화하려는 노력을 통해 효율을 높여야 함
      • 또한 자주 사용되는 Data에 대해서는 Global Memory를 이용하도록 프로그래밍 하는 것이 아닌, Shared Memory를 이용하도록 프로그래밍 하는 것이 좋음

배열의 index-Nf부터 index+Nf 까지의 원소의 합을 계산하는 코드를 통해 테스트

  • 개요
    • Shared Memory를 사용하는 경우, 각 thread에서는 자신이 속한 thread block에서 공유되는 원소들을 shared buffer로 옮겨주고 sync후에 이를 이용하여 결과를 계산
    • Global Memory는 Off Chip Memory이므로 속도가 느림
    • Shared Memory는 On Chip Memory이므로 Global Memory에 비해 속도가 빠름
    • 서로 인접한 thread에서는 사용하는 원소들이 거의 같음
      1. thread a가 담당하는 idx=i라고 하면, 인접한 thread b, c의 idx j, k는 i-1, i+1
      2. thread a에서는 [i-Nf, i+Nf]의 합을 구함
      3. thread b에서는 [j-Nf, j+Nf] = [i-Nf-1, i+Nf-1]의 합을 구함
      4. thread c에서는 [k-Nf, k+Nf] = [i-Nf+1, i+Nf+1]의 합을 구함
      5. 따라서 각 thread에서 공통적으로 사용되는 부분이 많기 때문에, 이를 Shared Memory로 옮겨서 처리하면 속도가 빠를 것임
      
  • 코드(Shared Memory를 이용하지 않는 경우)
    //배열의 index-Nf 부터 index+Nf 데이터 까지의 합을 계산하는 GPU코드(Shared Memory를 사용하지 않음)
    __global__ void sum_array_kernel_no_shared(IN int* d_arr, OUT int* d_sum_gpu, int N, int Nf) {
          const unsigned block_id = blockIdx.y * gridDim.x + blockIdx.x;
          const unsigned thread_id = threadIdx.y * blockDim.x + threadIdx.x;
          const unsigned id = block_id * BLOCK_SIZE + thread_id;
    
         //sum이라는 local변수를 사용하지 않은 이유는 sum local변수를 이용하게되면 레지스터를 활용하는 것이기 때문
         for (int i = -Nf; i <= Nf; i++) {
                  if (id + i >= N || id + i < 0) continue;
                        d_sum_gpu[id] += d_arr[id + i];
         }
    }
    
  • 코드(Shared Memory를 이용하는 경우)
    extern __shared__ int shared_buffer[];   //Shared Memory 동적 할당
    //배열의 index-Nf 부터 index+Nf 데이터 까지의 합을 계산하는 GPU코드(Shared Memory를 사용)
    __global__ void sum_array_kernel_shared(IN int* d_arr, OUT int* d_sum_gpu, int N, int Nf) {
          const unsigned block_id = blockIdx.y * gridDim.x + blockIdx.x;
          const unsigned thread_id = threadIdx.y * blockDim.x + threadIdx.x;
          const unsigned id = block_id * BLOCK_SIZE + thread_id;
          int i;
      
          //threadid가 0인 경우는 shared memory의 왼쪽 끝 부분을 채워 줘야 함
          if (thread_id == 0)
          {
                  for (i = 0; i < Nf; i++)
                  {
                          if (id + i < Nf) shared_buffer[i] = 0;
                          else shared_buffer[i] = d_arr[id + i - Nf];
                  }
          }
          //threadid가 BLOCK_SIZE-1인 경우는 shared memory의 오른쪽 끝 부분을 채워줘야 함
          if (thread_id == BLOCK_SIZE - 1)
          {
                  for (i = 1; i <= Nf; i++)
                  {
                          if (id + i >= N) shared_buffer[thread_id + i + Nf] = 0;
                          else shared_buffer[thread_id + i + Nf] = d_arr[id + i];
                  }
          }
          shared_buffer[thread_id + Nf] = d_arr[id];
          __syncthreads();//thread들의 sync를 맞추어줌
          for (i = 0; i <= 2 * Nf; i++)
          {
                 d_sum_gpu[id] += shared_buffer[thread_id + i];
          }
    }
    
  • 결과정리
    image
    • Off Chip인 Global Memory를 사용하는 것보다, On Chip인 Shared Memory를 사용하는 것이 속도가 빠른 것을 확인할 수 있음

code_optimization's People

Contributors

xfile6912 avatar

Stargazers

HakHyeon Song avatar Donghyuk Jung avatar

Watchers

James Cloos avatar  avatar

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.