5

我正在开发一个 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);

在这两种情况下,“计数”都被正确填充(我检查过)。但是,我的流式音频似乎出现了新问题——也许处理得不够快——这对我来说没有任何意义。显然,缓冲区代码被编译成超过三个 JVM 代码语句,但是当我开始这个时,它确实是一个合理的假设,即第二个版本会比第一个更快。

显然,我并没有坚持我的代码必须使用 Java NIO 缓冲区,但至少乍一看,这样做似乎有点像“斗鱼”。

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

谢谢,R。

4

3 回答 3

4

如果不是将数据包读入字节数组(将数据从本机缓冲区复制到数组中),然后将其包装在新的 ByteBuffer 中(创建一个新对象)并转换为 ShortBuffer(创建一个新对象)您只设置一次对象并避免复制。

您可以使用 DatagramChannel.socket() 创建您的套接字,然后像往常一样连接它并使用 socket.getChannel() 来获取 DatagramChannel 对象。此对象将允许您将数据包直接读取到现有的 ByteBuffer(您应该使用 ByteBuffer.allocateDirect 创建以实现最大效率)。然后,您可以只使用一次 asShortBuffer() 将您的数据视图创建为短裤,并在每次重新填充 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在所有可用的平台上都使用小端字节顺序,并且您上面的代码似乎是大端,所以这可能是不可能的为你),在这种情况下 shortBuf.get() 成为直接内存副本。

于 2012-05-11T08:28:29.553 回答
2

一般来说,直接使用原始类型将比使用对象更有效,因为您避免了创建对象、函数调用等的一些开销。

除了速度之外,使用实用对象还有其他原因:方便、安全等。

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

您还可以使用 Android 的分析器来查看您的问题到底出在哪里。请参阅TraceView

于 2010-10-28T00:43:47.437 回答
0

我会使用 DataInputStream 来完成这个任务,它包裹着一个包裹着字节数组的 ByteArrayInputStream。封装了 readShort() 中的移位,没有 ByteBuffer 的开销。

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

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

于 2010-10-29T02:29:29.727 回答