Warning: file_get_contents(/data/phpspider/zhask/data//catemap/6/cplusplus/129.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C++ 加速某些SSE2内部函数进行颜色转换_C++_Colors_Simd_Yuv_Sse2 - Fatal编程技术网

C++ 加速某些SSE2内部函数进行颜色转换

C++ 加速某些SSE2内部函数进行颜色转换,c++,colors,simd,yuv,sse2,C++,Colors,Simd,Yuv,Sse2,我正在尝试执行从YCbCr到BGRA的图像颜色转换(一点也不要问这个问题,这太令人头痛了) 无论如何,这需要尽可能快地执行,所以我使用编译器内部函数编写了它,以利用SSE2。这是我第一次进入SIMD领域,我基本上是一个初学者,所以我确信我做了很多没有效率的事情 我用于实际颜色转换的算术代码速度特别慢,英特尔的VTune显示这是一个严重的瓶颈 那么,我能不能加快下面的代码?它是在32位,一次4像素。我最初尝试用8位,一次16像素(就像在上面的循环中一样)进行计算,但是计算会导致整数溢出和转换中断。

我正在尝试执行从YCbCr到BGRA的图像颜色转换(一点也不要问这个问题,这太令人头痛了)

无论如何,这需要尽可能快地执行,所以我使用编译器内部函数编写了它,以利用SSE2。这是我第一次进入SIMD领域,我基本上是一个初学者,所以我确信我做了很多没有效率的事情

我用于实际颜色转换的算术代码速度特别慢,英特尔的VTune显示这是一个严重的瓶颈

那么,我能不能加快下面的代码?它是在32位,一次4像素。我最初尝试用8位,一次16像素(就像在上面的循环中一样)进行计算,但是计算会导致整数溢出和转换中断。整个过程,包括Intel jpeg解码,对于单个全高清字段,需要约14毫秒。如果我能把它降到至少12毫秒,最好是10毫秒,那就太好了

非常感谢您的帮助或提示。谢谢

const __m128i s128_8    = _mm_set1_epi8((char)128);


const int nNumPixels = roi.width * roi.height;

for (int i=0; i<nNumPixels; i+=32)
{
    // Go ahead and prefetch our packed UV Data.
    // As long as the load remains directly next, this saves us time.
    _mm_prefetch((const char*)&pSrc8u[2][i],_MM_HINT_T0); 

    // We need to fetch and blit out our k before we write over it with UV data.
    __m128i sK1 = _mm_load_si128((__m128i*)&pSrc8u[2][i]);
    __m128i sK2 = _mm_load_si128((__m128i*)&pSrc8u[2][i+16]);

    // Using the destination buffer temporarily here so we don't need to waste time doing a memory allocation.
    _mm_store_si128 ((__m128i*)&m_pKBuffer[i],      sK1);
    _mm_store_si128 ((__m128i*)&m_pKBuffer[i+16],   sK2);

    // In theory, this prefetch needs to be some cycles ahead of the first read. It isn't, yet it does appear to save us time. Worth investigating.
    _mm_prefetch((const char*)&pSrc8u[1][i],_MM_HINT_T0); 

    __m128i sUVI1 = _mm_load_si128((__m128i*)&pSrc8u[1][i]);
    __m128i sUVI2 = _mm_load_si128((__m128i*)&pSrc8u[1][i+16]);  

    // Subtract the 128 here ahead of our YCbCr -> BGRA conversion so we can go 16 pixels at a time rather than 4.
    sUVI1 = _mm_sub_epi8(sUVI1, s128_8);
    sUVI2 = _mm_sub_epi8(sUVI2, s128_8);

    // Swizzle and double up UV data from interleaved 8x1 byte blocks into planar
    __m128i sU1 = _mm_unpacklo_epi8(sUVI1, sUVI1);
    __m128i sV1 = _mm_unpackhi_epi8(sUVI1, sUVI1);

    __m128i sU2 = _mm_unpacklo_epi8(sUVI2, sUVI2);  
    __m128i sV2 = _mm_unpackhi_epi8(sUVI2, sUVI2);  

    _mm_store_si128((__m128i*)&pSrc8u[1][i],        sU1);
    _mm_store_si128((__m128i*)&pSrc8u[1][i+16],     sU2); 

    _mm_store_si128((__m128i*)&pSrc8u[2][i],        sV1);
    _mm_store_si128((__m128i*)&pSrc8u[2][i+16],     sV2); 
}

const __m128i s16   = _mm_set1_epi32(16);
const __m128i s299  = _mm_set1_epi32(299);
const __m128i s410  = _mm_set1_epi32(410);
const __m128i s518  = _mm_set1_epi32(518);
const __m128i s101  = _mm_set1_epi32(101);
const __m128i s209  = _mm_set1_epi32(209);

Ipp8u* pDstP = pDst8u;
for (int i=0; i<nNumPixels; i+=4, pDstP+=16)
{
    __m128i sK = _mm_set_epi32(m_pKBuffer[i],           m_pKBuffer[i+1],            m_pKBuffer[i+2],            m_pKBuffer[i+3]);

    __m128i sY = _mm_set_epi32(pSrc8u[0][i],            pSrc8u[0][i+1],             pSrc8u[0][i+2],             pSrc8u[0][i+3]);
    __m128i sU = _mm_set_epi32((char)pSrc8u[1][i],      (char)pSrc8u[1][i+1],       (char)pSrc8u[1][i+2],       (char)pSrc8u[1][i+3]);
    __m128i sV = _mm_set_epi32((char)pSrc8u[2][i],      (char)pSrc8u[2][i+1],       (char)pSrc8u[2][i+2],       (char)pSrc8u[2][i+3]);

    // N.b. - Attempted to do the sub 16 in 8 bits similar to the sub 128 for U and V - however doing it here is quicker
    // as the time saved on the arithmetic is less than the time taken by the additional loads/stores needed in the swizzle loop
    sY = _mm_mullo_epi32(_mm_sub_epi32(sY, s16), s299);

    __m128i sR  = _mm_srli_epi32(_mm_add_epi32(sY,_mm_mullo_epi32(s410, sV)), 8);
    __m128i sG  = _mm_srli_epi32(_mm_sub_epi32(_mm_sub_epi32(sY, _mm_mullo_epi32(s101, sU)),_mm_mullo_epi32(s209, sV)), 8);
    __m128i sB  = _mm_srli_epi32(_mm_add_epi32(sY, _mm_mullo_epi32(s518, sU)), 8);

    //Microsoft's YUV Conversion
    //__m128i sC = _mm_sub_epi32(sY, s16);
    //__m128i sD = _mm_sub_epi32(sU, s128);
    //__m128i sE = _mm_sub_epi32(sV, s128);
    //
    //__m128i sR =  _mm_srli_epi32(_mm_add_epi32(_mm_add_epi32(_mm_mullo_epi32(s298, sC), _mm_mullo_epi32(s409, sE)), s128), 8);
    //__m128i sG    = _mm_srli_epi32(_mm_add_epi32(_mm_sub_epi32(_mm_mullo_epi32(s298, sC), _mm_sub_epi32(_mm_mullo_epi32(s100, sD), _mm_mullo_epi32(s208, sE))), s128), 8);
    //__m128i sB    = _mm_srli_epi32(_mm_add_epi32(_mm_add_epi32(_mm_mullo_epi32(s298, sC), _mm_mullo_epi32(s516, sD)), s128), 8);

    __m128i sKGl = _mm_unpacklo_epi32(sK, sG);
    __m128i sKGh = _mm_unpackhi_epi32(sK, sG);

    __m128i sRBl = _mm_unpacklo_epi32(sR, sB);
    __m128i sRBh = _mm_unpackhi_epi32(sR, sB);

    __m128i sKRGB1 = _mm_unpackhi_epi32(sKGh,sRBh);
    __m128i sKRGB2 = _mm_unpacklo_epi32(sKGh,sRBh);
    __m128i sKRGB3 = _mm_unpackhi_epi32(sKGl,sRBl);
    __m128i sKRGB4 = _mm_unpacklo_epi32(sKGl,sRBl);

    __m128i p1 = _mm_packus_epi16(sKRGB1, sKRGB2);
    __m128i p2 = _mm_packus_epi16(sKRGB3, sKRGB4);

    __m128i po = _mm_packus_epi16(p1, p2);

    _mm_store_si128((__m128i*)pDstP, po);
}
const\uuum128i s128\u8=\umm\uset1\uepi8((char)128);
const int nNumPixels=roi.width*roi.height;
对于(int i=0;i BGRA)转换,我们可以一次转换16个像素,而不是4个像素。
sUVI1=_mm_sub_epi8(sUVI1,s128_8);
sUVI2=_mm_sub_epi8(sUVI2,s128_8);
//将交叉8x1字节块中的UV数据旋转并加倍到平面
__m128i sU1=_mm_unplo_epi8(sUVI1,sUVI1);
__m128i sV1=_mm_unpachi_epi8(sUVI1,sUVI1);
__m128i sU2=_mm_unplo_epi8(sUVI2,sUVI2);
__m128i sV2=_mm_unpachi_epi8(sUVI2,sUVI2);
_mm_store_si128((_m128i*)和pSrc8u[1][i],sU1);
_mm_store_si128((_m128i*)和pSrc8u[1][i+16],sU2);
_mm_store_si128((_m128i*)和pSrc8u[2][i],sV1);
_mm_store_si128((_m128i*)和pSrc8u[2][i+16],sV2);
}
常数m128i s16=_mm_设置1_epi32(16);
常数m128i s299=_mm_set1_epi32(299);
常数m128i s410=_mm_设置1_epi32(410);
常数m128i s518=_mm_set1_epi32(518);
常数m128i s101=_mm_设置1_epi32(101);
常数m128i s209=_mm_设置1_epi32(209);
Ipp8u*pDstP=pDst8u;

对于(int i=0;i您可能在这里受到带宽限制,因为相对于加载和存储的数量,计算量非常小

一个建议是:去掉
\u mm_预取
内部函数-它们几乎肯定没有帮助,甚至可能会妨碍在较新的CPU上运行(这些CPU在自动预取方面已经做得很好)

另一个需要关注的领域:

__m128i sK = _mm_set_epi32(m_pKBuffer[i],           m_pKBuffer[i+1],            m_pKBuffer[i+2],            m_pKBuffer[i+3]);
__m128i sY = _mm_set_epi32(pSrc8u[0][i],            pSrc8u[0][i+1],             pSrc8u[0][i+2],             pSrc8u[0][i+3]);
__m128i sU = _mm_set_epi32((char)pSrc8u[1][i],      (char)pSrc8u[1][i+1],       (char)pSrc8u[1][i+2],       (char)pSrc8u[1][i+3]);
__m128i sV = _mm_set_epi32((char)pSrc8u[2][i],      (char)pSrc8u[2][i+1],       (char)pSrc8u[2][i+2],       (char)pSrc8u[2][i+3]);

这会产生很多不必要的指令-你应该在这里使用
\u mm\u load\u xxx
\u mm\u解压xxx\u xxx
。它看起来像更多的代码,但效率会更高。你可能应该在循环的每次迭代中处理16个像素,而不是4个像素-这样你就可以一次加载8位值的向量,然后解包,根据需要将每组4个值作为32位整数的向量来获取。

是否使用IntelC编译此文件?我尝试过IntelC和MSVC++并对每个值的设置进行了实验-intel编译器速度稍快,但差异可以忽略不计(最多约1/4毫秒)。我希望算法的改进能带来更大的成功。嗨,保罗,谢谢你的回答。我知道预取指令通常会阻碍而不是帮助,但很多测量都说它们会稍微加快速度。我会记住这一点,尽管我会继续。关于另一个建议,谢谢:)我会试一试。@Ali Parr:预取非常依赖于CPU——不过,过去5年(Core 2 Duo之后)的任何东西都可能在不受不明智的预取本质的干扰的情况下,自己做得更好即使在旧的CPU上,也很难提前足够长的时间开始预取以产生任何有用的效果。@Paul R-完全接受这一点,只是我的指标似乎有所不同。我处于一个令人羡慕的位置,我知道这段代码将在(i7)上运行的确切硬件。但我一定会知道的。谢谢你的提示-我正在做一个重构,因为我们说。。。匡威:)你好,保罗。工作完成了。删除所有set_epi32指令,并替换为加载/解包/洗牌。它看起来确实有更多的代码,而且效率更高。完美的建议,让我从14毫秒降到12毫秒,这是一个非常有帮助的加速。谢谢@阿里:谢谢你的更新-我很高兴听到这对你来说很好