C 钳位短到无符号字符
我有一个简单的C函数,如下所示:C 钳位短到无符号字符,c,C,我有一个简单的C函数,如下所示: unsigned char clamp(short value){ if (value < 0) return 0; if (value > 0xff) return 0xff; return value; } 编辑3: 刚刚在FFmpeg代码中看到了这一点的一个非常好的实现: /** * Clip a signed integer value into the 0-255 range. * @param a value
unsigned char clamp(short value){
if (value < 0) return 0;
if (value > 0xff) return 0xff;
return value;
}
编辑3:
刚刚在FFmpeg代码中看到了这一点的一个非常好的实现:
/**
* Clip a signed integer value into the 0-255 range.
* @param a value to clip
* @return clipped value
*/
static av_always_inline av_const uint8_t av_clip_uint8_c(int a)
{
if (a&(~0xFF)) return (-a)>>31;
else return a;
}
这当然有效,如果很好的话,它会减少到1 如果您只是想避免实际的If/else,请使用
?:代码>操作员:
return value < 0 ? 0 : (value > 0xff ? 0xff : value);
返回值<0?0:(值>0xff?0xff:值);
然而,就效率而言,这应该没有什么不同
在实践中,你不应该为这样琐碎的事情担心效率。让编译器来进行优化。使其高效的一种方法是将此函数声明为内联函数,以避免函数调用开销。您也可以使用第三级运算符将其转换为宏,但这将删除编译器的返回类型检查。您可以使用另一张海报所示的?:
或使用abs()
的有趣属性来执行此操作,该属性允许您计算两个值的最大值或最小值
例如,表达式(a+abs(a))/2
对于正数返回a
,否则返回0
(最大值为a
和0
)
这给
unsigned char clip(short value)
{
short a = (value + abs(value)) / 2;
return (a + 255 - abs(a - 255)) / 2;
}
为了让自己相信这是可行的,这里有一个测试程序:
#include <stdio.h>
unsigned char clip(short value)
{
short a = (value + abs(value)) / 2;
return (a + 255 - abs(a - 255)) / 2;
}
void test(short value)
{
printf("clip(%d) = %d\n", value, clip(value));
}
int main()
{
test(0);
test(10);
test(-10);
test(255);
test(265);
return 0;
}
当然,有人可能会说,在abs()
中可能有一个测试,但是gcc-O3
可以线性编译它:
clip:
movswl %di, %edi
movl %edi, %edx
sarl $31, %edx
movl %edx, %eax
xorl %edi, %eax
subl %edx, %eax
addl %edi, %eax
movl %eax, %edx
shrl $31, %edx
addl %eax, %edx
sarl %edx
movswl %dx, %edx
leal 255(%rdx), %eax
subl $255, %edx
movl %edx, %ecx
sarl $31, %ecx
xorl %ecx, %edx
subl %ecx, %edx
subl %edx, %eax
movl %eax, %edx
shrl $31, %edx
addl %edx, %eax
sarl %eax
ret
但请注意,这将比原始函数效率低得多,原始函数编译为:
clip:
xorl %eax, %eax
testw %di, %di
js .L1
movl $-1, %eax
cmpw $255, %di
cmovle %edi, %eax
.L1:
rep
ret
但至少它回答了你的问题:)你可以做一个2D查找表:
unsigned char clamp(short value)
{
static const unsigned char table[256][256] = { ... }
const unsigned char x = value & 0xff;
const unsigned char y = (value >> 8) & 0xff;
return table[y][x];
}
当然,这看起来很奇怪(这是一个64 KB的表,用于这个琐碎的计算)。然而,考虑到您提到要在GPU上执行此操作,我认为上面的内容可能是纹理查找,我相信在GPU上非常快速
此外,如果您的GPU使用OpenGL,您当然可以直接使用内置的
:
clamp(value, 0, 255);
这不会进行类型转换(GLSL中似乎没有8位整数类型),但仍然如此。假设短两个字节,并以代码可读性为代价:
clipped_x = (x & 0x8000) ? 0 : ((x >> 8) ? 0xFF : x);
你写你想要避免在GPU上分支。的确,在并行环境中,分支可能非常昂贵,因为要么必须对两个分支进行评估,要么必须应用同步。但是,如果分支足够小,代码将比大多数算法更快。报告描述了原因:
有时,编译器可能[…]
优化out if或switch语句
而是使用分支谓词。
在这些情况下,任何扭曲都无法实现
偏离[……]
当使用分支谓词时,没有
其执行的指令
取决于控制条件
被跳过。相反,它们都是
与每线程条件关联
设置为true的代码或谓词
或基于控制的错误
条件和条件
指令被安排在
执行时,只有带有
真谓词实际上是
执行。用假密码的指示
谓词不写入结果,并且
也不要计算地址或读取
操作数
分支预测很快。太快了!如果您查看由优化编译器生成的中间PTX代码,您会发现它甚至优于普通的算法。因此,davmac答案中的代码可能是最快的
我知道您没有特别询问CUDA,但大多数最佳实践指南也适用于OpenCL,可能还有大部分AMDs GPU编程
顺便说一句:在我见过的几乎所有GPU代码中,大部分时间都花在内存访问上,而不是算术上。确保你的个人资料 你应该给这个难看但仅限于算术的版本计时
unsigned char clamp(short value){
short pmask = ((value & 0x4000) >> 7) | ((value & 0x2000) >> 6) |
((value & 0x1000) >> 5) | ((value & 0x0800) >> 4) |
((value & 0x0400) >> 3) | ((value & 0x0200) >> 2) |
((value & 0x0100) >> 1);
pmask |= (pmask >> 1) | (pmask >> 2) | (pmask >> 3) | (pmask >> 4) |
(pmask >> 5) | (pmask >> 6) | (pmask >> 7);
value |= pmask;
short nmask = (value & 0x8000) >> 8;
nmask |= (nmask >> 1) | (nmask >> 2) | (nmask >> 3) | (nmask >> 4) |
(nmask >> 5) | (nmask >> 6) | (nmask >> 7);
value &= ~nmask;
return value;
}
那么:
unsigned char clamp (short value) {
unsigned char r = (value >> 15); /* uses arithmetic right-shift */
unsigned char s = !!(value & 0x7f00) * 0xff;
unsigned char v = (value & 0xff);
return (v | s) & ~r;
}
但我严重怀疑它的执行速度是否比涉及分支的原始版本快。请简要说明一下术语,我相信这通常被称为钳位,而不是钳位,如果与计算相关,则通常被称为“饱和算术”,如果你在谷歌搜索,可能会有帮助。你为什么要这么做?这看起来已经很有效了。GPU上有abs()
吗?@roe,我已经更新了术语。Thanx。不确定这是否是您的意图,但FFmpeg的最后一个示例看起来像是进行了换行。所以正如我提到的,我希望去掉分支。因此,这与if/else相同。如果在目标体系结构上可能的话,编译器可能会优化掉分支。编译器优化不会更容易或更难?:但是。它只是一个if-else,语法混乱。最有可能发明?:操作符是为了节省存储源代码文件的硬盘空间。。。所以除非你的硬盘不能处理更多的字节,否则就没有理由使用?:abs还会增加额外的函数调用开销。我认为abs有一个小于零的检查值。@Shailesh Kumar:事实上,GCC在这种情况下不会为abs()
生成函数调用或测试,如编辑后的版本所示。有趣的是,Nvidia的CUTIL头使用常规的max/min函数来实现这一点!表查找当然是一种可能的解决方案。我得检查一下它的性能是否比if/else好。?和:本质上是分支。thanx。我应该花时间阅读CUDA C最佳实践指南!
unsigned char clamp(short value){
short pmask = ((value & 0x4000) >> 7) | ((value & 0x2000) >> 6) |
((value & 0x1000) >> 5) | ((value & 0x0800) >> 4) |
((value & 0x0400) >> 3) | ((value & 0x0200) >> 2) |
((value & 0x0100) >> 1);
pmask |= (pmask >> 1) | (pmask >> 2) | (pmask >> 3) | (pmask >> 4) |
(pmask >> 5) | (pmask >> 6) | (pmask >> 7);
value |= pmask;
short nmask = (value & 0x8000) >> 8;
nmask |= (nmask >> 1) | (nmask >> 2) | (nmask >> 3) | (nmask >> 4) |
(nmask >> 5) | (nmask >> 6) | (nmask >> 7);
value &= ~nmask;
return value;
}
unsigned char clamp (short value) {
unsigned char r = (value >> 15); /* uses arithmetic right-shift */
unsigned char s = !!(value & 0x7f00) * 0xff;
unsigned char v = (value & 0xff);
return (v | s) & ~r;
}