性能:浮动到int转换和剪切结果到范围

我正在使用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”就完全按照我想要的方式完成) 。

对于范围内的值,您可以将两个比较转换为一个。 这可以使成本减半。 目前,如果值太过负,则只执行一次比较。 (这可能不是你的典型案例)

 if (sample + 0x7fff8000 < 0x7fff0000) sample = sample < 0 ? -32768 : 32767; 

当你将int转换为short时,你永远不会获得剪切function,比特会被截断,然后被读作short。 例如(短)-40000变为25536,而不是预期的-32768。

可能你必须编辑你的问题,我相信如果你反汇编字节码就知道了。 此外,还有一个JIT编译器可以优化此代码(因为它经常被调用)到平台相关指令。

请将此答案转换为评论。

float to int conversions是你可以在x86处理器上执行的最慢的操作之一,因为它需要修改x87舍入模式(两次),这会对处理器进行序列化和刷新。 如果你可以使用SSE指令而不是x87指令,你可以获得相当大的加速,但我不知道是否有办法在java中做到这一点。 也许尝试使用x86_64 JVM?

这是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 

这种分支可能会杀了你,但也许这提供了一些有用的东西? 这个趋势为零。

int sample =((int)floatval)&0xffff;