ByteBuffer/IntBuffer/ShortBuffer Java 类速度快吗?
我正在开发一个 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。
I'm working on an Android application (in Java, obviously) and I recently updated my UDP reader code. In both versions, I set up some buffers and receive a UDP packet:
byte[] buf = new byte[10000];
short[] soundData = new short[1000];
DatagramPacket packet = new DatagramPacket (buf, buf.length);
socket.receive (packet);
In the initial version, I put the data back together one byte at a time (it's actually 16 PCM audio data):
for (int i = 0; i < count; i++)
soundData[i] = (short) (((buf[k++]&0xff) << 8) + (buf[k++]&0xff));
In the updated version, I used some cool Java tools I didn't know about when I started:
bBuffer = ByteBuffer.wrap (buf);
sBuffer = bBuffer.asShortBuffer();
sBuffer.get (soundData, 0, count);
In both cases, "count" is being populated correctly (I checked). However, there appear to be new problems with my streaming audio -- perhaps it isn't being handled fast enough -- which doesn't make any sense to me. Obviously, the buffer code is compiling into a lot more than three statements of JVM code, but it sure seemed like a reasonable assumption when I start this that the 2nd version would be faster than the 1st.
Patently, I'm not insisting that my code HAS to use Java NIO buffers, but at first glance at least, it DOES seem like a mo' betta' to go about this.
Anybody got any recommendations for a fast, simple Java UDP reader and whether there is a generally accepted "best way"??
Thanks,
R.
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(3)
一般来说,直接使用基本类型比使用对象更有效,因为您可以避免创建对象、函数调用等的一些开销。
除了速度之外,还有其他原因使用实用程序对象:方便、安全、等等。
在这种特殊情况下测试差异的最佳方法是实际测量它。使用大数据集尝试这两种方法并计时。然后,您可以决定在这种情况下是否值得获得这些好处。
您还可以使用 Android 的分析器来查看问题的真正所在。请参阅 TraceView。
In general, working with primitive types directly is going to be more efficient than working with objects because you avoid some of the overhead of creating objects, function calls, etc.
There are reasons to use the utility objects other than speed: convenience, safety, etc.
The best way to test the difference in this particular case would be to actually measure it. Try out both methods with a large dataset and time it. Then, you can decide if it is worth the benefits in this case.
You can also use Android's profiler to see where your problems really are. See TraceView.
我将使用 DataInputStream 来完成此任务,将其包裹在字节数组周围的 ByteArrayInputStream 中。将移位封装在 readShort() 中,而无需 ByteBuffer 的开销。
或者,您可以通过 DatagramChannel 直接读入 DirectByteBuffer,而不是使用 DatagramPacket 和 DatagramPacket。数据报套接字。目前您正在使用 java.net 和 java.nio 的混合体。
我不认为这两种方法之间存在重大性能差异,但我希望它们都比您的混合方法更快。
I would use DataInputStream for this task, wrapped around a ByteArrayInputStream wrapped around the byte array. Encapsulates the shifting in readShort() without the overheads of ByteBuffer.
Alternatively you could read directly into a DirectByteBuffer via a DatagramChannel, rather than using DatagramPacket & DatagramSocket. At present you're using a mixture of java.net and java.nio.
I wouldn't expect major performance differences between these two approaches, but I would expect them both to be faster than your hybrid approach.
如果不是将数据包读入字节数组(将数据从本机缓冲区复制到数组中),然后将其包装在新的 ByteBuffer 中(创建一个新对象)并转换为 ShortBuffer(创建一个新对象)您只设置一次对象并避免了复制。
您可以通过使用 DatagramChannel.socket() 创建套接字,然后照常连接它并使用 socket.getChannel() 获取 DatagramChannel 对象来完成此操作。该对象将允许您将数据包直接读入现有的 ByteBuffer(您应该使用 ByteBuffer.allocateDirect 创建它以获得最大效率)。然后,您可以仅使用一次 asShortBuffer() 将数据创建为 Shorts 视图,并在每次重新填充 ByteBuffer 后从该 ShortBuffer 中读取数据。
因此,代码如下所示:
您应该发现这比以前的代码更有效,因为它避免了数据的整个副本,并且格式转换是通过手动优化的代码来处理的,而不是由编译器生成的字节操作来处理,这可能不是最佳的。如果您使用平台本机字节顺序,效率会更高(我相信Android在所有可用平台上使用小端字节顺序,并且上面的代码似乎是大端字节顺序,所以这可能是不可能的对于你来说),在这种情况下,shortBuf.get() 就变成了直接内存复制。
Your code would be more efficient if instead of reading a packet into a byte array (copying the data from a native buffer into the array) and then wrapping it in a new ByteBuffer (creating a new object) and converting to a ShortBuffer (creating a new object) you set up your objects only once and avoided the copy.
You can do this by using DatagramChannel.socket() to create your socket, then connecting it as usual and usuing socket.getChannel() to get a DatagramChannel object. This object will allow you to read packets directly into an existing ByteBuffer (which you should create with ByteBuffer.allocateDirect for maximum efficiency). You can then us asShortBuffer() just once to create a view of your data as shorts, and read from that ShortBuffer after every time you refill the ByteBuffer.
The code therefore looks like this:
You should find this is much more efficient than your previous code because it avoids an entire copy of the data and the format conversion is handled by hand-optimized code rather than compiler generated byte manipulation which may be suboptimal. It will be somewhat more efficient if you use the platform-native byte order (I believe Android uses little-endian byte order on all platforms it is available for, and your code above seems to be big-endian, so this may not be possible for you), in which case shortBuf.get() becomes a direct memory copy.