我正在研究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上。
答案 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的混合。
我不希望这两种方法之间存在显着的性能差异,但我希望它们都比你的混合方法更快。