0

关于java性能的一个简单问题。如果我写一个循环

for(int i=0;i<n;++i) buffer[(k++)%buffer.length]=something;

其中有些东西是一个非平凡的数字滤波器。使用此代码,我在每次写入时都有一个模运算。这感觉有点傻,因为 Java VM 无论如何都会检查它。因此,我假设使用 ArrayIndexOutOfBounds 的构造会更快(缓冲区包含 1'000'000 个数字,所以我们不会经常出现溢出)

int i;
try
  {
  for(i=0;i<n;++i,++k) buffer[k]=something;
  }
catch (ArrayIndexOutOfBounds e)
  {
  k=0;
  for(;i<n;++i,++k) buffer[k]=something;
  }

第三种解决方案可能是提前计算我们会溢出的点,然后手动将循环一分为二。确定循环可以走多远的代码每 768 个样本执行一次,因此从这个角度来看,它可能比 catch 方法慢。

这里的问题是,除了愚蠢的代码重复(我很乐意牺牲在性能的祭坛上)之外,我们还有更多的代码。在那里,Java 的优化通常不如较小的例程。

所以我的问题是:什么策略最有效?有人对这种结构有经验吗?另外,任何人都可以了解这两种结构在 android 设备上的性能吗?

4

1 回答 1

3

您的答案取决于您的目标平台。你已经添加了 Android 标签,所以我将用 Dalvik 和(比方说)Nexus 4 来回答。

首先,ARMv7-A 架构不提供整数除法指令。每次通过循环时,您的模数都会在软件中计算出来,这会降低您的速度。(这就是为什么最好对哈希表使用 2 的幂大小——您可以使用位掩码而不是 mod。)

其次,抛出异常是昂贵的。VM 必须创建异常对象,并使用当前堆栈的快照对其进行初始化。除了直接的开销之外,您还创建了 X 个稍后必须清理的对象,并增加了 VM 不得不在计算过程中阻止您并收集垃圾的可能性。

第三,一般而言,您可以从内部循环中提取的任何计算都代表着胜利,因此在每次循环迭代时手动测试数组溢出是不令人满意的。如果可以避免,您不想在循环头或正文中添加对kvs.的测试。length(JIT 编译器可能会做这样的事情——如果它可以告诉数组索引永远不会离开数组的末尾,它就不必进行每个元素的边界检查。)

Based on the (still slightly vague) sense of what you're doing and how many times you're doing it, I'd say the best option is to compute the "break" position ahead of the loop, and iterate the necessary number of times.

I'm curious to know how this turns out in practice. :-)

于 2012-12-14T01:02:48.650 回答