Iphone 用氖灯优化RGBA8888到RGB565的转换
我正在尝试使用NEON vector指令集优化iOS上的图像格式转换。我认为这会很好地映射到这一点,因为它处理大量类似的数据 不过,我的尝试并没有那么成功,与朴素的c实现相比,只实现了少量的加速: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
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提高缓存命中率
- 无需转换为“长”
- 循环中的指令更少
/*
* 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 );