내 커널 성능에 영향을 미치는 근본적인 요인을 파악할 수 없습니다. 두 개의 이미지를로드하고 두 이미지를로드하는 두 개의 간단한 커널과 두 이미지를로드하는 두 개의 간단한 커널을 구현했으며 비트 감에서 ANDS을 추가했습니다. 이제 커널을 템플릿 화하여 커널이 8 비트 및 32 비트 이미지는 물론 1, 3, 4 채널 이미지를 사용할 수있게했습니다.CUDA의 성능
그래서 처음에 나는 모두 커널 uchar4
등 내가 때문에 병합의, 그러나, 트리플 사용에 대한 너무 확신하지 못했습니다과 함께 uchar3
및 float3
으로 전역 메모리로드를했다, 그래서 나는 그것을 줄 거라고 생각 프로파일 링 실행. 나는 작업이 채널 번호와 독립적 이었기 때문에 실제로는 uchar3
이미지가 아니라 1 채널 uchar
이미지가 3 배 넓이 인 것처럼 이미지를 읽을 수 있다고 생각했습니다.
실제로 uchar3
전역로드는 로드보다 훨씬 느립니다. 나의 노력은 옳았다. 그러나 슬프게도 이것은 산술 커널에서만 발생했습니다. 비트 AND 연산은 정확한 결과를 보여줍니다!
자, 이제 uchar
이 아닌 uint
s 이미지 데이터를로드 할 수 있다는 것을 알고 있습니다. 비트 작업의 경우 완벽하게 병합 처리해야합니다. 그러나 나는 단순히 무슨 일이 일어나고 있는지 배우고 이해하기를 원한다고 가정 해 봅시다.
그리고 float3
및 float4
등을 잊어 보겠습니다. 내 문제는 커널의 uchar
버전입니다. 따라서 간단히 말해 uchar
로드가 uchar3
로드보다 더 빠르며 때로는로드되지 않는 경우가 있습니까?
저는 GTX 470의 컴퓨팅 기능 2.0을 사용하고 있습니다.
추신. CUDA 프로그래밍 가이드에 따르면 논리 연산과 추가 연산은 동일한 처리량을가집니다. (제 커널은 실제로 uchar
을 uint
으로 변환해야만합니다.하지만 두 커널 모두에서 커널이 발생해야합니다.) 그래서 실행 길이는 내가 모은 것과 거의 같아야합니다.
산술 (uchar
버전) 커널을 추가
__global__ void add_8uc1(uchar* inputOne, uchar* inputTwo, uchar* output, unsigned int width, unsigned int height, unsigned int widthStep)
{
const int xCoordinateBase = blockIdx.x * IMAGE_X * IMAGE_MULTIPLIER + threadIdx.x;
const int yCoordinate = blockIdx.y * IMAGE_Y + threadIdx.y;
if (yCoordinate >= height)
return;
#pragma unroll IMAGE_MULTIPLIER
for (int i = 0; i < IMAGE_MULTIPLIER && xCoordinateBase + i * IMAGE_X < width; ++i)
{
// Load memory.
uchar* inputElementOne = (inputOne + yCoordinate * widthStep + (xCoordinateBase + i * IMAGE_X + threadIdx.x));
uchar* inputElementTwo = (inputTwo + yCoordinate * widthStep + (xCoordinateBase + i * IMAGE_X + threadIdx.x));
// Write output.
*(output + yCoordinate * widthStep + (xCoordinateBase + i * IMAGE_X + threadIdx.x)) = inputElementOne[0] + inputElementTwo[0];
}
}
비트 단위 AND 커널 :
__global__ void and_8uc1(uchar* inputOne, uchar* inputTwo, uchar* output, unsigned int width, unsigned int height, unsigned int widthStep)
{
const int xCoordinateBase = blockIdx.x * IMAGE_X * IMAGE_MULTIPLIER + threadIdx.x;
const int yCoordinate = blockIdx.y * IMAGE_Y + threadIdx.y;
if (yCoordinate >= height)
return;
#pragma unroll IMAGE_MULTIPLIER
for (int i = 0; i < IMAGE_MULTIPLIER && xCoordinateBase + i * IMAGE_X < width; ++i)
{
// Load memory.
uchar* inputElementOne = (inputOne + yCoordinate * widthStep + (xCoordinateBase + i * IMAGE_X + threadIdx.x));
uchar* inputElementTwo = (inputTwo + yCoordinate * widthStep + (xCoordinateBase + i * IMAGE_X + threadIdx.x));
// Write output.
*(output + yCoordinate * widthStep + (xCoordinateBase + i * IMAGE_X + threadIdx.x)) = inputElementOne[0] & inputElementTwo[0];
}
}
uchar3
버전
// Load memory.
uchar3 inputElementOne = *reinterpret_cast<uchar3*>(inputOne + yCoordinate * widthStep + (xCoordinateBase + i * IMAGE_X + threadIdx.x) * 3);
uchar3 inputElementTwo = *reinterpret_cast<uchar3*>(inputTwo + yCoordinate * widthStep + (xCoordinateBase + i * IMAGE_X + threadIdx.x) * 3);
// Write output.
*reinterpret_cast<uchar3*>(output + yCoordinate * widthStep + (xCoordinateBase + i * IMAGE_X + threadIdx.x) * 3)
= make_uchar3(inputElementOne.x + inputElementTwo.x, inputElementOne.y + inputElementTwo.y, inputElementOne.z + inputElementTwo.z);
마찬가지로 AND 커널. (나는 커널을 정확히 기억하는지 모르지만, 솔직히 말해서 나는 내일 그것을 확증 할 것이다.) SM에의 명령어 세트에는 24 비트로드가 없기 때문에
커널을 표시 할 수 있습니까? 알 수없는 코드에 대해 판단하기가 어렵습니다. 그리고 실행 시간이 어떻게 비교되는지 (즉, 커널이 한 버전이나 다른 버전의 비슷한 시간이 걸리므로 더 빠릅니다 ...). – Grizzly
간결한 질문을 드릴 수 있습니까? 왜'uchar'를로드하는 것이'uchar3'을로드하는 것보다 더 빠를 수 있는지 알고 싶습니까? CUDA 5의 프로파일 러는 가장 기본적인 유형의 프로파일 링 실행에 대해서조차 uncoalesced load/stores가 문제라면 통지합니다. 2 건의 사례에 대해 백분율로 말한 것은 무엇입니까? –
적어도 uchar 경우에는로드가 선형이며 완벽하게 병합됩니다. 나는 지금 일하고 있지 않기 때문에 커널을 붙여 넣을 수 없습니다. 왜 때로는 더 빠르며 때로는 그렇지 않은지 알고 싶습니다. –