ByteBuffer / IntBuffer / ShortBuffer Java类是否快速?

时间:2010-10-27 21:25:17

标签: java android nio bytebuffer

我正在研究Android应用程序(显然是Java),最近我更新了我的UDP阅读器代码。在这两个版本中,我都设置了一些缓冲区并接收UDP数据包:

byte[] buf = new byte[10000];
short[] soundData = new short[1000];
DatagramPacket packet = new DatagramPacket (buf, buf.length);
socket.receive (packet);

在初始版本中,我将数据一次放回一个字节(实际上是16个PCM音频数据):

for (int i = 0; i < count; i++)
    soundData[i] = (short) (((buf[k++]&0xff) << 8) + (buf[k++]&0xff));

在更新版本中,我使用了一些我开始时不知道的很酷的Java工具:

bBuffer  = ByteBuffer.wrap (buf);
sBuffer  = bBuffer.asShortBuffer();
sBuffer.get (soundData, 0, count);

在这两种情况下,“count”正在正确填充(我检查过)。然而,我的流媒体音频似乎出现了新的问题 - 也许它的处理速度不够快 - 这对我来说没有任何意义。显然,缓冲区代码编译成了三个以上的JVM代码语句,但是当我开始这样做时,第二个版本会比第一个版本更快,这似乎是一个合理的假设。

显而易见,我并不是说我的代码必须使用Java NIO缓冲区,但至少乍一看,看起来似乎很难做到这一点。

任何人都对快速,简单的Java UDP阅读器以及是否有普遍接受的“最佳方式”有任何建议?

谢谢, R上。

3 个答案:

答案 0 :(得分:3)

您的代码将是如果代替读出分组划分为字节阵列(从天然缓冲器中的数据复制到该阵列),然后在新的ByteBuffer它包裹(创建一个新对象),并转换到ShortBuffer更高效(创建一个新对象)只设置一次对象并避免复制。

您可以使用DatagramChannel.socket()创建套接字,然后像往常一样连接它并使用socket.getChannel()来获取DatagramChannel对象。此对象将允许您将数据包直接读入现有的ByteBuffer(您应该使用ByteBuffer.allocateDirect创建,以获得最大效率)。您可以将我们asShortBuffer()只创建一次以创建数据视图作为short,并在每次重新填充ByteBuffer后从该ShortBuffer中读取。

因此代码如下所示:

 DatagramSocket socket = DatagramChannel.socket();
 // code to connect socket
 DatagramChannel channel = socket.getChannel();
 ByteBuffer buffer = ByteBuffer.allocateDirect (10000);
 // you may want to invoke buffer.order(...) here to tell it what byte order to use
 ShortBuffer shortBuf = buffer.asShortBuffer();

 // in your receive loop:
 buffer.clear();
 channel.receive(buffer);
 shortBuf.position(0).limit(buffer.position()/2); // may ignore a byte if odd number received
 shortBuf.get(soundBuf,0,shortBuf.limit());

您应该发现这比以前的代码更有效,因为它避免了整个数据副本,并且格式转换由手动优化代码处理,而不是编译器生成的字节操作,这可能是次优的。这将在一定程度上更有效,如果您使用的平台的本地字节顺序(我相信的Android在所有平台上它可用于使用little-endian字节顺序,你上面的代码似乎是大端,所以这是不可能的对你而言),在这种情况下,shortBuf.get()成为直接的内存副本。

答案 1 :(得分:1)

通常,直接使用基本类型比使用对象更有效,因为您可以避免创建对象,函数调用等的一些开销。

有理由使用速度以外的实用程序对象:方便性,安全性等。

在这种特殊情况下测试差异的最佳方法是实际测量它。尝试使用大型数据集的两种方法并计时。然后,您可以决定在这种情况下是否值得获益。

您还可以使用Android的分析器查看问题的确切位置。请参阅TraceView

答案 2 :(得分:0)

我会使用DataInputStream完成此任务,包裹在字节数组周围的ByteArrayInputStream。在没有ByteBuffer的开销的情况下封装readShort()中的移位。

或者,您可以通过DatagramChannel直接读取DirectByteBuffer,而不是使用DatagramPacket&amp; DatagramSocket的。目前你正在使用java.net和java.nio的混合。

我不希望这两种方法之间存在显着的性能差异,但我希望它们都比你的混合方法更快。