我正在使用float进行一些音频处理。结果需要转换回PCM样本,我注意到从float到int的转换是非常昂贵的。 更令人沮丧的是,我需要将结果剪辑到短路(-32768到32767)的范围内。 虽然我通常会明确地假设这可以通过简单地将float转换为short来确保,但这在Java中失败,因为在字节码级别它导致F2I后跟I2S。所以不是简单的:
int sample = (short) flotVal;
我需要诉诸这个丑陋的序列:
int sample = (int) floatVal;
if (sample > 32767) {
sample = 32767;
} else if (sample < -32768) {
sample = -32768;
}
有更快的方法吗?
(约占运行总时间的约6%似乎花费在铸造上,而乍一看似乎没有那么多6%,当我认为处理部分涉及大量矩阵乘法和IDCT时,它令人震惊)
编辑上面的强制转换/剪辑代码(不出意料地)在循环体中,它从float []中读取浮点值并将它们放入byte []中。我有一个测试套件,可以测量几个测试用例的总运行时间(处理大约200MB的原始音频数据)。当通过将循环索引分配给样本来替换强制转换赋值“int sample =(int)floatVal”时,6%是从运行时差异中得出的结论。
编辑@leopoldkot:我知道Java中的截断,如原始问题(F2I,I2S字节码序列)中所述。我只是简单地尝试了强制转换,因为我认为Java有一个F2S字节码,但遗憾的是它没有(最初来自68K汇编背景,其中一个简单的“fmove.w FP0,D0”就完全符合我的要求)
答案 0 :(得分:2)
对于范围内的值,您可以将两个比较转换为一个。这可以使成本减半。目前,如果值太过负,则只执行一次比较。 (这可能不是你的典型案例)
if (sample + 0x7fff8000 < 0x7fff0000)
sample = sample < 0 ? -32768 : 32767;
答案 1 :(得分:1)
当你将int转换为short时,你永远不会得到剪切功能,比特被截断然后被读作短。 例如。 (短)-40000变为25536,而不是你预期的-32768。
可能你必须编辑你的问题,如果你反汇编字节码,我相信你知道的。 此外,还有一个JIT编译器可以优化此代码(因为它经常被调用)到平台相关指令。
请将此回答转换为评论。
答案 2 :(得分:0)
float to int转换是你可以在x86处理器上执行的最慢的操作之一,因为它需要修改x87舍入模式(两次),这会对处理器进行序列化和刷新。如果你可以使用SSE指令而不是x87指令,你可以获得相当大的加速,但我不知道是否有办法在java中做到这一点。也许尝试使用x86_64 JVM?
答案 3 :(得分:0)
这是Python,但应该很容易转换。我不知道浮点运算的成本有多高,但如果你能将它保存在整数寄存器中,你可能会有一些提升;这假设您可以将IEEE754位重新解释为int。 (这就是我命名为float2hex的名字。)
import struct
def float2hex(v):
s = struct.pack('f', v)
h = struct.unpack('I', s)[0]
return h
def ToInt(f):
h = float2hex(f)
s = h >> 31
exp = h >> 23 & 0xFF
mantissa = h & 0x7FFFFF
exp = exp - 126
if exp >= 16:
if s:
v = -32768
else:
v = 32767
elif exp < 0:
v = 0
else:
v = mantissa | (1 << 23)
exp -= 24
if exp > 0:
v = v << exp
elif exp < 0:
v = v >> -exp
if s:
v = -v
print v
这种分支可能会杀了你,但也许这提供了一些有用的东西?这个趋势为零。
答案 4 :(得分:-1)
int sample =((int)floatval)&amp; 0xFFFF的;