문제

바이트 버퍼 (0 ~ 255)를 플로트 버퍼 (0.0 ~ 1.0)로 변환하려면 어떻게해야합니까? 물론 두 값 사이에는 관계가 있어야합니다. 예를 들어 : 0은 바이트 버퍼에서 0.0.f가 플로트 버퍼에서 .0.f, 바이트 버퍼는 플로트 버퍼에서 .5f, 바이트 버퍼의 255는 1.f가됩니다. 플로트 버퍼.

실제로 이것은 내가 가진 코드입니다.

for (int y=0;y<height;y++) {
    for (int x=0;x<width;x++) {
        float* floatpixel = floatbuffer + (y * width + x) * 4;
        BYTE* bytepixel = (bytebuffer + (y * width + x) * 4);
        floatpixel[0] = bytepixel[0]/255.f;
        floatpixel[1] = bytepixel[1]/255.f;
        floatpixel[2] = bytepixel[2]/255.f;
        floatpixel[3] = 1.0f; // A
    }
}

이것은 매우 느리게 실행됩니다. 내 친구가 전환 테이블을 사용하라고 제안했지만 다른 사람이 다른 접근 방식을 줄 수 있는지 알고 싶었습니다.

도움이 되었습니까?

해결책

조회 테이블을 사용하든 아니든, 코드는 실제로 모든 루프 반복을 많이 할 필요가없는 많은 작업을 수행하고 있습니다.

포인터가 제한을 제한하고 Const에서만 읽은 포인터를 선언하십시오. 내부 루프의 각 반복에서 포인터를 계산하지 말고 초기 값을 계산하고 증가시키지 마십시오. 내부 루프를 몇 번 뽑으십시오. 대상이 지원하는 경우 벡터 SIMD 작업을 사용하십시오. 최대 값과 증가하고 비교하지 말고 대신 0과 비교하십시오.

같은 것

float* restrict floatpixel = floatbuffer;
BYTE const* restrict bytepixel = bytebuffer;
for( int size = width*height; size > 0; --size )
{
    floatpixel[0] = bytepixel[0]*(1.f/255.f);
    floatpixel[1] = bytepixel[1]*(1.f/255.f);
    floatpixel[2] = bytepixel[2]*(1.f/255.f);
    floatpixel[3] = 1.0f; // A
    floatpixel += 4;
    bytepixel += 4;
}

시작이 될 것입니다.

다른 팁

나는 이것이 오래된 질문이라는 것을 알고 있지만, 아무도 IEEE 플로트 표현을 사용하여 해결책을주지 않았기 때문에 여기에 하나가 있습니다.

// Use three unions instead of one to avoid pipeline stalls
union { float f; uint32_t i; } t, u, v, w;
t.f = 32768.0f;
float const b = 256.f / 255.f;

for(int size = width * height; size > 0; --size)
{
    u.i = t.i | bytepixel[0]; floatpixel[0] = (u.f - t.f) * b;
    v.i = t.i | bytepixel[1]; floatpixel[1] = (v.f - t.f) * b;
    w.i = t.i | bytepixel[2]; floatpixel[2] = (w.f - t.f) * b;
    floatpixel[3] = 1.0f; // A
    floatpixel += 4;
    bytepixel += 4;
}

이것은 그 이상입니다 두 배 빠른 로서 int 에게 float 내 컴퓨터의 변환 (Core 2 Duo CPU).

다음은 한 번에 16 개의 플로트를 수행하는 위 코드의 SSE3 버전입니다. 필요합니다 bytepixel 그리고 floatpixel 128 비트 정렬되고 총 크기는 4의 배수입니다. 나는 이것이 지시적으로 진행하는 가장 짧은 방법이라고 생각하지만, 컴파일러가 충분히 영리하지 않으면 잠복하고 손으로 일정을 잡기를 원할 수 있습니다.

/* Magic values */
__m128i zero = _mm_set_epi32(0, 0, 0, 0);
__m128i magic1 = _mm_set_epi32(0xff000000, 0xff000000, 0xff000000, 0xff000000);
__m128i magic2 = _mm_set_epi32(0x47004700, 0x47004700, 0x47004700, 0x47004700);
__m128 magic3 = _mm_set_ps(32768.0f, 32768.0f, 32768.0f, 32768.0f);
__m128 magic4 = _mm_set_ps(256.0f / 255.0f, 256.0f / 255.0f, 256.0f / 255.0f, 256.0f / 255.0f);

for(int size = width * height / 4; size > 0; --size)
{
    /* Load bytes in vector and force alpha value to 255 so that
     * the output will be 1.0f as expected. */
    __m128i in = _mm_load_si128((__m128i *)bytepixel);
    in = _mm_or_si128(in, magic1);

    /* Shuffle bytes into four ints ORed with 32768.0f and cast
     * to float (the cast is free). */
    __m128i tmplo = _mm_unpacklo_epi8(in, zero);
    __m128i tmphi = _mm_unpackhi_epi8(in, zero);
    __m128 in1 = _mm_castsi128_ps(_mm_unpacklo_epi16(tmplo, magic2));
    __m128 in2 = _mm_castsi128_ps(_mm_unpackhi_epi16(tmplo, magic2));
    __m128 in3 = _mm_castsi128_ps(_mm_unpacklo_epi16(tmphi, magic2));
    __m128 in4 = _mm_castsi128_ps(_mm_unpackhi_epi16(tmphi, magic2));

    /* Subtract 32768.0f and multiply by 256.0f/255.0f */
    __m128 out1 = _mm_mul_ps(_mm_sub_ps(in1, magic3), magic4);
    __m128 out2 = _mm_mul_ps(_mm_sub_ps(in2, magic3), magic4);
    __m128 out3 = _mm_mul_ps(_mm_sub_ps(in3, magic3), magic4);
    __m128 out4 = _mm_mul_ps(_mm_sub_ps(in4, magic3), magic4);

    /* Store 16 floats */
    _mm_store_ps(floatpixel, out1);
    _mm_store_ps(floatpixel + 4, out2);
    _mm_store_ps(floatpixel + 8, out3);
    _mm_store_ps(floatpixel + 12, out4);

    floatpixel += 16;
    bytepixel += 16;
}

편집하다: 사용하여 정확도를 향상시킵니다 (f + c/b) * b 대신에 f * b + c.

편집하다: SSE3 버전을 추가하십시오.

이를 위해 정적 조회 테이블을 사용하십시오. 컴퓨터 그래픽 회사에서 일할 때 우리는 프로젝트와 연결된 하드 코드 조회 테이블을 얻었습니다.

병목 현상이 무엇인지 알아야합니다.

  • 데이터 테이블을 '잘못된'방향으로 반반하면 끊임없이 캐시 미스를 누르십시오. 조회는 그 일을 해결하는 데 도움이되지 않습니다.
  • 프로세서가 찾는 것보다 스케일링이 느려지면 조회 테이블이 캐시에 맞는 경우 찾아 보면 성능을 향상시킬 수 있습니다.

또 다른 팁 :

struct Scale {
    BYTE operator()( const float f ) const { return f * 1./255; }
};
std::transform( float_table, float_table + itssize, floatpixel, Scale() );

예, 조회 테이블은 루프에서 많은 부서를 수행하는 것보다 확실히 빠릅니다. 256 미리 계산 된 플로트 값의 테이블을 생성하고 바이트 값을 사용하여 해당 테이블을 색인하십시오.

인덱스 계산을 제거하여 루프를 조금 최적화하고 다음과 같은 작업을 수행 할 수도 있습니다.

float *floatpixel = floatbuffer;
BYTE *bytepixel = bytebuffer;

for (...) {
  *floatpixel++ = float_table[*bytepixel++];
  *floatpixel++ = float_table[*bytepixel++];
  *floatpixel++ = float_table[*bytepixel++];
  *floatpixel++ = 1.0f;
}

룩업 테이블은 변환하는 가장 빠른 방법입니다 :) 여기에서 간다 :

byte_to_float.h 파일을 생성하는 Python 코드 :

#!/usr/bin/env python

def main():
    print "static const float byte_to_float[] = {"

    for ii in range(0, 255):
        print "%sf," % (ii/255.0)

    print "1.0f };"    
    return 0

if __name__ == "__main__":
    main()

전환을 얻기위한 C ++ 코드 :

floatpixel[0] = byte_to_float[ bytepixel[0] ];

단순하지 않습니까?

매번 1/255를 계산하지 마십시오. 컴파일러가 이것을 제거하기에 충분히 똑똑할지 모르겠습니다. 한 번 계산하고 매번 다시 적용하십시오. 더 좋은 점은 상수로 정의하십시오.

라이센스 : CC-BY-SA ~와 함께 속성
제휴하지 않습니다 StackOverflow
scroll top