【问题标题】:Why are Java native buffers that slow?为什么 Java 本机缓冲区那么慢?
【发布时间】:2014-07-27 01:04:36
【问题描述】:

我正在尝试优化我在 Android 设备上运行的简单区域增长算法。最初我使用ArrayList 来保存属于一个区域的点。每个点都由Point 实例描述,需要为每个点实例化。算法用了大约 15 秒在大约 1 兆像素的图像中找到所有区域。

Point 类很简单:

class Point
{
    public int x, y;
}

我认为我可以通过减少 Point 实例化的数量来减少计算时间。所以我用Region 类替换了点列表,该类利用本机缓冲区进行点登记:

private int capacity;
private int pointsCount = 0;

private ByteBuffer buffer;
private IntBuffer intBufferView;

根据点数计算所需的字节缓冲区大小:

private static int getByteBufferSize( int capacity )
{
    // 4 bytes per integer and 2 integers per point
    return capacity * 4 * 2;
}

我选择的初始容量为100,但我也尝试将其设置为10,即ArrayList的初始容量,并尝试将其设置为我测试图像中最大区域的大小:

public Region()
{
    this.capacity = 100;
    this.buffer = ByteBuffer.allocateDirect( getByteBufferSize( 100 ) );
    this.intBufferView = buffer.asIntBuffer();
}

为了向区域添加点,我使用了这种方法:

public void add( final Point point )
{
    if( pointsCount >= capacity )
    {
        grow();
    }

    final int offset = 2 * pointsCount;
    intBufferView.put( offset + 0, point.x );
    intBufferView.put( offset + 1, point.y );
    ++pointsCount;
}

对于读取某个点,由索引标识,这个方法:

public void fetchPoint( Point p, int pointIndex )
{
    final int offset = 2 * pointIndex;
    p.x = intBufferView.get( offset + 0 );
    p.y = intBufferView.get( offset + 1 );
}

我实施了与ArrayList 使用的策略相同的增长策略:

private void grow()
{
    capacity = ( capacity * 3 ) / 2 + 1;
    final int bufferSize = getByteBufferSize( capacity );
    final ByteBuffer newBuffer = ByteBuffer.allocateDirect( bufferSize );
    newBuffer.put( buffer );
    newBuffer.rewind();
    buffer = newBuffer;
    intBufferView = buffer.asIntBuffer();
}

但是,通过这种优化,区域增长算法需要大约 33 秒才能在同一张图像上完成。这是我无法解释的性能下降。是我的实现,整个想法,还是这里有什么问题?

【问题讨论】:

  • 我之前使用过 ByteBuffer,它不需要接近毫秒(甚至可能像你的情况一样接近几秒)来操作。检查您自己的算法 - 99.99% 的时间问题会出现在某个地方,而在 99.99% 的时间问题会出现在您在此处发布的某些代码中。
  • 我敢打赌,> 90% 的时间消耗(在显示的代码中)都花在了 grow() 中。分配和复制数据是昂贵的部分。
  • @theV0ID 也许是因为这个问题的决定性的,变相咆哮的语气? ;-)

标签: java android performance algorithm image-processing


【解决方案1】:

坦率地说,我认为整个想法都是错误的。 Buffer 类针对与您不同的用户案例进行了优化;即在 I/O 设备之间传输数据(广义上。

如果您想要更好的(比当前的)性能,请将Buffer 替换为int[]。我预测您会看到较小的性能下降(与ArrayList<Point> 版本相比),但您仍然会看到下降。这就是你为节省内存付出的代价。

【讨论】:

  • 同意,通常不是误用导致问题,而不是实际系统和/或标准功能。
  • 感谢您的澄清。剩下一题。与ArrayList<Point> 相比,您预测int[] 的性能会下降,对吧?我试过了,它给了我性能提升:整个分段现在需要 大约 7-8 秒。我想知道是否有可能通过 Region 的 JNI 实现来获得更好的性能。
  • 预测是这样的……我是个悲观主义者。
  • JNI 为您提供更好性能的唯一方法是,在本机代码中进行区域计算比在 Java JNI 本机调用开销中损失的更多。
  • @maartinus - 你在谈论一个不同的用例。在这种情况下,我希望 int[]IntBuffer 的位置属性是相同的。与平台无关。 (我相信您正在考虑使用映射缓冲区而不是对象集合的情况。这不是这里发生的事情......)
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-05-10
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多