Iphone 用氖灯优化RGBA8888到RGB565的转换

Iphone 用氖灯优化RGBA8888到RGB565的转换,iphone,ios,assembly,arm,neon,Iphone,Ios,Assembly,Arm,Neon,我正在尝试使用NEON vector指令集优化iOS上的图像格式转换。我认为这会很好地映射到这一点,因为它处理大量类似的数据 不过,我的尝试并没有那么成功,与朴素的c实现相比,只实现了少量的加速: for(int i = 0; i < pixelCount; ++i, ++inPixel32) { const unsigned int r = ((*inPixel32 >> 0 ) & 0xFF); const unsigned int g = ((*i

我正在尝试使用NEON vector指令集优化iOS上的图像格式转换。我认为这会很好地映射到这一点,因为它处理大量类似的数据

不过,我的尝试并没有那么成功,与朴素的c实现相比,只实现了少量的加速:

for(int i = 0; i < pixelCount; ++i, ++inPixel32) {
    const unsigned int r = ((*inPixel32 >> 0 ) & 0xFF);
    const unsigned int g = ((*inPixel32 >> 8 ) & 0xFF);
    const unsigned int b = ((*inPixel32 >> 16) & 0xFF);
    *outPixel16++ = ((r >> 3) << 11) | ((g >> 2) << 5) | ((b >> 3) << 0);
}

完整的代码可在我会感谢任何帮助,谢谢

您可能希望使用vld4q_u8()而不是vld4_u8(),并相应地调整其余代码。很难说问题出在哪里,但汇编程序在其他方面看起来并不太糟糕。

(我不熟悉NEON,也不太熟悉Ipad2的内存系统,但这是我们过去对88110像素运算所做的,它是今天SIMD扩展的早期先驱)

内存延迟有多大


当手臂从内存中提取“下一个”值时,你能通过展开内部循环并对“上一个”值运行霓虹灯指令来隐藏它吗?简单浏览一下NEON手册,就可以并行运行ARM和NEON指令。

我不认为将vld4_u8转换为vld4q_u8会带来任何性能改进

代码似乎很简单。我不擅长ASM,因此需要一些时间来深入研究它

霓虹灯看起来很简单。但是我不确定是否使用了r5_g6_b5|=g16而不是vorrq_u16

请也看看优化级别。据我所知,neon代码优化级别最高为1。因此,当参考代码和霓虹灯代码都考虑默认优化时,性能可能会有所不同,因为默认情况下参考的优化级别可能会有所不同


我在neon中找不到任何能更好地处理当前代码的地方。

这是为XCode手工优化的neon实现:

/* IT DOESN'T WORK!!! USE THE NEXT VERSION BELOW.
 * BGRA2RGB565.s
 *
 * Created by Jake "Alquimista" Lee on 11. 11. 1..
 * Copyright 2011 Jake Lee. All rights reserved.
 */


    .align 2
    .globl _bgra2rgb565_neon
    .private_extern _bgra2rgb565_neon

// unsigned int * bgra2rgb565_neon(unsigned int * pDst, unsigned int * pSrc, unsigned int count);


//ARM
pDst        .req    r0
pSrc        .req    r1
count       .req    r2

//NEON
blu         .req    d16
grn         .req    d17
red         .req    d18
alp         .req    d19
rg          .req    red
gb          .req    blu

_bgra2rgb565_neon:
    pld     [pSrc]
    tst     count, #0x7
    movne   r0, #0
    bxne    lr

loop:
    pld     [pSrc, #32]
    vld4.8  {blu, grn, red, alp}, [pSrc]!
    subs    count, count, #8
    vshr.u8 red, red, #3
    vext.8  rg, grn, red, #5
    vshr.u8 grn, grn, #2
    vext.8  gb, blu, grn, #3
    vst2.8  {gb, rg}, [pDst]!
    bgt     loop

    bx      lr
此版本将比您建议的快很多倍:

  • 通过PLD提高缓存命中率

  • 无需转换为“长”

  • 循环中的指令更少

不过仍有一些优化空间,您可以修改循环,使其每次迭代转换16个像素,而不是8个像素。 然后,您可以安排指令以完全避免这两个暂停(这在上面的8/迭代版本中根本不可能),并从NEON的双问题功能中获益

我没有这样做,因为这会使代码难以理解

知道VEXT应该做什么很重要

现在由你决定。:)

我验证了这段代码是在Xcode下正确编译的。 虽然我很确定它也能正常工作,但我不能保证这一点,因为我没有测试环境。 如果出现故障,请告诉我。那我就相应地改正它

赛亚

==============================================================================

这是改进版

由于VSRI指令的性质不允许目标以外的两个操作数,因此无法创建一个更健壮的寄存器分配操作数

请检查源图像的图像格式。(元素的精确字节顺序)

如果不是B、G、R、A,这是iOS上的默认和本机转换,那么您的应用程序将受到iOS内部转换的严重影响

如果出于任何原因根本不可能改变这一点,请告诉我。 我将编写一个与之匹配的新版本

PS:我忘了在函数原型的开头删除下划线。现在它不见了

/*
 * BGRA2RGB565.s
 *
 * Created by Jake "Alquimista" Lee on 11. 11. 1..
 * Copyright 2011 Jake Lee. All rights reserved.
 *
 * Version 1.1
 * - bug fix
 *
 * Version 1.0
 * - initial release
 */


    .align 2
    .globl _bgra2rgb565_neon
    .private_extern _bgra2rgb565_neon

// unsigned int * bgra2rgb565_neon(unsigned int * pDst, unsigned int * pSrc, unsigned int count);


//ARM
pDst        .req    r0
pSrc        .req    r1
count       .req    r2

//NEON
blu         .req    d16
grn         .req    d17
red         .req    d18
alp         .req    d19

gb          .req    grn
rg          .req    red

_bgra2rgb565_neon:
    pld     [pSrc]
    tst     count, #0x7
    movne   r0, #0
    bxne    lr

.loop:
    pld     [pSrc, #32]
    vld4.8  {blu, grn, red, alp}, [pSrc]!
    subs    count, count, #8

    vsri.8  red, grn, #5
    vshl.u8 gb, grn, #3
    vsri.8  gb, blu, #3

    vst2.8  {gb, rg}, [pDst]!
    bgt     .loop

    bx      lr

如果您使用的是iOS或OS X,那么您可能会很高兴在Accelerate.framework中发现vImageConvert_RGBA8888toRGB565()和朋友。此函数将8位值舍入为最接近的565值

要获得更好的抖动效果(抖动质量几乎无法与8位颜色区分),请尝试vImageConvert_AnyToAny():


这两种方法中的任何一种都将进行矢量化和多线程处理以获得最佳性能。

oops,现在我看到使用VSRI将获得更好的性能:非常感谢您的实现!它看起来肯定比我的快,但是当我把它放在Xcode项目中时,我的测试失败了。这段代码现在被签入到一个分支中,但它似乎没有返回正确的结果。我来看看……问题可能是由字节顺序引起的。如果逐字节读取像素,iOS的默认字节顺序是B、G、R、A。其他任何东西都会导致内部转换,从而消耗宝贵的周期。请检查您使用的图像格式。同时,我将使用VSRI而不是VEXT创建一个新版本,并且在字节顺序方面更加健壮。很快见。我在函数原型中犯了一个错误。下划线现在被删除了。让ARM与NEON并行工作听起来很诱人,但这不是很实际——如果不是不可能的话。虽然ARM2NEON传输速度很快,但NEON2ARM非常慢。从理论上讲,可以让它们与独立的数据块并行工作,但NEON比ARM在每一条指令上都能做得更多,以至于ARM在这几个周期内几乎无法完成任何事情。虽然循环控件、条件分支等都是免费的,但这很好。谢谢Jake,知道我在不久的将来什么时候开始真正的NEONing会很有用。
/* IT DOESN'T WORK!!! USE THE NEXT VERSION BELOW.
 * BGRA2RGB565.s
 *
 * Created by Jake "Alquimista" Lee on 11. 11. 1..
 * Copyright 2011 Jake Lee. All rights reserved.
 */


    .align 2
    .globl _bgra2rgb565_neon
    .private_extern _bgra2rgb565_neon

// unsigned int * bgra2rgb565_neon(unsigned int * pDst, unsigned int * pSrc, unsigned int count);


//ARM
pDst        .req    r0
pSrc        .req    r1
count       .req    r2

//NEON
blu         .req    d16
grn         .req    d17
red         .req    d18
alp         .req    d19
rg          .req    red
gb          .req    blu

_bgra2rgb565_neon:
    pld     [pSrc]
    tst     count, #0x7
    movne   r0, #0
    bxne    lr

loop:
    pld     [pSrc, #32]
    vld4.8  {blu, grn, red, alp}, [pSrc]!
    subs    count, count, #8
    vshr.u8 red, red, #3
    vext.8  rg, grn, red, #5
    vshr.u8 grn, grn, #2
    vext.8  gb, blu, grn, #3
    vst2.8  {gb, rg}, [pDst]!
    bgt     loop

    bx      lr
/*
 * BGRA2RGB565.s
 *
 * Created by Jake "Alquimista" Lee on 11. 11. 1..
 * Copyright 2011 Jake Lee. All rights reserved.
 *
 * Version 1.1
 * - bug fix
 *
 * Version 1.0
 * - initial release
 */


    .align 2
    .globl _bgra2rgb565_neon
    .private_extern _bgra2rgb565_neon

// unsigned int * bgra2rgb565_neon(unsigned int * pDst, unsigned int * pSrc, unsigned int count);


//ARM
pDst        .req    r0
pSrc        .req    r1
count       .req    r2

//NEON
blu         .req    d16
grn         .req    d17
red         .req    d18
alp         .req    d19

gb          .req    grn
rg          .req    red

_bgra2rgb565_neon:
    pld     [pSrc]
    tst     count, #0x7
    movne   r0, #0
    bxne    lr

.loop:
    pld     [pSrc, #32]
    vld4.8  {blu, grn, red, alp}, [pSrc]!
    subs    count, count, #8

    vsri.8  red, grn, #5
    vshl.u8 gb, grn, #3
    vsri.8  gb, blu, #3

    vst2.8  {gb, rg}, [pDst]!
    bgt     .loop

    bx      lr
vImage_CGImageFormat RGBA8888Format = 
{
    .bitsPerComponent = 8,
    .bitsPerPixel = 32,
    .bitmapInfo = kCGBitmapByteOrderDefault | kCGImageAlphaNoneSkipLast,
    .colorSpace = NULL,  // sRGB or substitute your own in
};

vImage_CGImageFormat RGB565Format = 
{
    .bitsPerComponent = 5,
    .bitsPerPixel = 16,
    .bitmapInfo = kCGBitmapByteOrder16Little | kCGImageAlphaNone,
    .colorSpace = RGBA8888Format.colorSpace,  
};


err = vImageConverterRef converter = vImageConverter_CreateWithCGImageFormat(
         &RGBA8888Format, &RGB565Format, NULL, kvImageNoFlags, &err );

err = vImageConvert_AnyToAny( converter, &src, &dest, NULL, kvImageNoFlags );