- html - 出于某种原因,IE8 对我的 Sass 文件中继承的 html5 CSS 不友好?
- JMeter 在响应断言中使用 span 标签的问题
- html - 在 :hover and :active? 上具有不同效果的 CSS 动画
- html - 相对于居中的 html 内容固定的 CSS 重复背景?
我有一个客户端服务器应用程序,其中服务器向客户端发送一些二进制数据,并且客户端必须根据自定义二进制格式从该字节流中反序列化对象。数据通过HTTPS连接发送,并且客户端使用HttpsURLConnection.getInputStream()
读取数据。
我实现了一个DataDeserializer
,它使用一个InputStream
并将其完全反序列化。它的工作方式是使用较小的缓冲区(通常少于100个字节)执行多个inputStream.read(buffer)
调用。为了获得更好的整体性能,我还在这里尝试了不同的实现。一项更改确实显着提高了此类的性能(我现在使用ByteBuffer
来读取基本类型,而不是通过字节移位手动进行操作),但是与网络流结合使用时,不会出现差异。有关更多详细信息,请参见以下部分。
我的问题的快速摘要
即使我证明网络和解串器本身都很快,但从网络流进行反序列化仍然花费了很长时间。我可以尝试一些常见的性能技巧吗?我已经用BufferedInputStream
包装了网络流。另外,我尝试了双缓冲并取得了一些成功(请参见下面的代码)。任何获得更好性能的解决方案都是值得欢迎的。
性能测试方案
在我的测试方案中,服务器和客户端位于同一台计算机上,服务器发送约174 MB的数据。可以在本文结尾处找到代码片段。您在此处看到的所有数字均为5次测试运行的平均值。
首先,我想知道InputStream
中的HttpsURLConnection
可以读取的速度。包裹到BufferedInputStream
中后,花费了26.250s的时间将全部数据写入ByteArrayOutputStream
.1
然后,我测试了解串器的性能,将其全部174 MB作为ByteArrayInputStream
传递了。在我改进解串器的实现之前,它花费了38.151s。改进后只花了23.466s.2
我想就是这样,但是没有。
我实际上想要做的是将connection.getInputStream()
传递给解串器。奇怪的是:在反序列化器改进之前,反序列化花费了61.413s,而在改进之后是60.100s!3
怎么会这样尽管解串器明显改善,但这里几乎没有任何改善。另外,与该改进无关,我感到惊讶的是,这花费的时间比单独的性能总结所花费的时间更长(60.100> 26.250 + 23.466)。为什么?不要误会我的意思,我没想到这会是最好的解决方案,但是我也没想到它会那么糟糕。
因此,需要注意三件事:
整体速度受网络约束,至少需要26.250s。也许有一些我可以调整的http设置,或者我可以进一步优化服务器,但是就目前而言,这可能不是我应该关注的重点。
我的解串器实现很可能仍不完美,但单靠它比网络快,因此我认为没有必要进一步改进它。
基于1.和2。我假设应该以某种方式可以组合起来完成整个工作(从网络中读取+反序列化),而该过程不会超过26.250s。欢迎提供有关如何实现此目标的任何建议。
我正在寻找某种双缓冲,允许两个线程从中读取并并行写入。
在标准Java中是否有类似的东西?最好是从InputStream
继承的某些类允许并行写入?如果有类似的东西,但不是从InputStream
继承的,我也许也可以更改我的DataDeserializer
以从该对象消费。
由于我没有找到任何这样的DoubleBufferInputStream
,因此我自己实现了它。
该代码很长,可能并不完美,我不想打扰您为我做代码审查。它具有两个16kB缓冲区。使用它,我能够将整体性能提高到39.885s.4
那比60.100s好得多,但比26.250s还差很多。选择不同的缓冲区大小并没有太大变化。因此,我希望有人可以引导我实现一些好的双缓冲区实现。
测试代码
1(26.250s)
InputStream inputStream = new BufferedInputStream(connection.getInputStream());
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
byte[] buffer = new byte[16 * 1024];
int count = 0;
long start = System.nanoTime();
while ((count = inputStream.read(buffer)) >= 0) {
outputStream .write(buffer, 0, count);
}
long end = System.nanoTime();
InputStream inputStream = new ByteArrayInputStream(entire174MBbuffer);
DataDeserializer deserializer = new DataDeserializer(inputStream);
long start = System.nanoTime();
deserializer.Deserialize();
long end = System.nanoTime();
InputStream inputStream = new BufferedInputStream(connection.getInputStream());
DataDeserializer deserializer = new DataDeserializer(inputStream);
long start = System.nanoTime();
deserializer.Deserialize();
long end = System.nanoTime();
MyDoubleBufferInputStream doubleBufferInputStream = new MyDoubleBufferInputStream();
new Thread(new Runnable() {
@Override
public void run() {
try (InputStream inputStream = new BufferedInputStream(connection.getInputStream())) {
byte[] buffer = new byte[16 * 1024];
int count = 0;
while ((count = inputStream.read(buffer)) >= 0) {
doubleBufferInputStream.write(buffer, 0, count);
}
} catch (IOException e) {
} finally {
doubleBufferInputStream.closeWriting(); // read() may return -1 now
}
}
}).start();
DataDeserializer deserializer = new DataDeserializer(doubleBufferInputStream);
long start = System.nanoTime();
deserializer.deserialize();
long end = System.nanoTime();
prepareForRead()
,它执行流的实际读取。
class DataDeserializer {
private InputStream _stream;
private ByteBuffer _buffer;
public DataDeserializer(InputStream stream) {
_stream = stream;
_buffer = ByteBuffer.allocate(256 * 1024);
_buffer.order(ByteOrder.LITTLE_ENDIAN);
_buffer.flip();
}
private int readInt() throws IOException {
prepareForRead(4);
return _buffer.getInt();
}
private long readLong() throws IOException {
prepareForRead(8);
return _buffer.getLong();
}
private CustomObject readCustomObject() throws IOException {
prepareForRead(/*size of CustomObject*/);
int customMember1 = _buffer.getInt();
long customMember2 = _buffer.getLong();
// ...
return new CustomObject(customMember1, customMember2, ...);
}
// several other built-in and custom object read methods
private void prepareForRead(int count) throws IOException {
while (_buffer.remaining() < count) {
if (_buffer.capacity() - _buffer.limit() < count) {
_buffer.compact();
_buffer.flip();
}
int read = _stream.read(_buffer.array(), _buffer.limit(), _buffer.capacity() - _buffer.limit());
if (read < 0)
throw new EOFException("Unexpected end of stream.");
_buffer.limit(_buffer.limit() + read);
}
}
public HugeCustomObject Deserialize() throws IOException {
while (...) {
// call several of the above methods
}
return new HugeCustomObject(/* deserialized members */);
}
}
InputStream inputStream = new BufferedInputStream(connection.getInputStream());
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
byte[] buffer = new byte[16 * 1024];
long read = 0;
long write = 0;
while (true) {
long t1 = System.nanoTime();
int count = istream.read(buffer);
long t2 = System.nanoTime();
read += t2 - t1;
if (count < 0)
break;
t1 = System.nanoTime();
ostream.write(buffer, 0, count);
t2 = System.nanoTime();
write += t2 - t1;
}
System.out.println(read + " " + write);
ByteArrayOutputStream
则仅需要0.817s。这是有道理的,因为这两个数字几乎完美地合计为先前测得的26.250s(加上一些额外的测量开销)。
MyDoubleBufferInputStream doubleBufferInputStream = new MyDoubleBufferInputStream();
new Thread(new Runnable() {
@Override
public void run() {
try (InputStream inputStream = new BufferedInputStream(httpChannelOutputStream.getConnection().getInputStream(), 256 * 1024)) {
byte[] buffer = new byte[16 * 1024];
long read = 0;
long write = 0;
while (true) {
long t1 = System.nanoTime();
int count = inputStream.read(buffer);
long t2 = System.nanoTime();
read += t2 - t1;
if (count < 0)
break;
t1 = System.nanoTime();
doubleBufferInputStream.write(buffer, 0, count);
t2 = System.nanoTime();
write += t2 - t1;
}
System.out.println(read + " " + write);
} catch (IOException e) {
} finally {
doubleBufferInputStream.closeWriting();
}
}
}).start();
DataDeserializer deserializer = new DataDeserializer(doubleBufferInputStream);
deserializer.deserialize();
read
变量的值为39.294s(这怎么可能?与前面示例中测量的代码完全相同,为25.756s!)*,而写入我的双缓冲区仅需0.096s。同样,这些数字几乎可以完美地概括为代码段4的测量时间。
run()
方法中,而这40s中的100%是CPU时间。另一方面,它在解串器内部也要花费40秒,但是这里CPU时间只有26秒,而等待时间只有14秒。这与从网络读取到
ByteBufferOutputStream
的时间完全匹配。因此,我想我必须改进双缓冲区的“缓冲区切换算法”。
最佳答案
显然,我的“错误”是使用32位JVM(精确的是jre1.8.0_172)。
在64位版本的JVM和tadaaa上运行完全相同的代码片段...在那儿运行起来非常快捷而且很有意义。
特别是,请参见这些新数字以获取相应的代码段:
片段1:4.667秒(对比26.250秒)
摘要2:11.568秒(相对于23.466秒)
片段3:17.185秒(相比60.100秒)
片段4:12.336秒(相对于39.885秒)
因此,显然,给Does Java 64 bit perform better than the 32-bit version?的答案不再是真的。或者,此特定的32位JRE版本中存在严重的错误。我还没有测试其他任何人。
如您所见,#4仅比#2慢一点,这完全符合我最初的假设
基于1.和2.我假设应该以某种方式
以合并的方式完成整个工作(从网络中读取+
反序列化),最多不超过26.250s。
同样,我的问题的更新2中描述的分析方法的非常奇怪的结果也不再发生。我还没有在64位中重复执行每个测试,但是现在我所做的所有分析结果都是合理的,即无论哪个代码段,相同的代码都花费相同的时间。因此,也许这确实是一个错误,或者有人有合理的解释吗?
关于java - 如何提高从HttpsURLConnection.getInputStream()反序列化对象的性能?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/51020591/
我正在编写一个具有以下签名的 Java 方法。 void Logger(Method method, Object[] args); 如果一个方法(例如 ABC() )调用此方法 Logger,它应该
我是 Java 新手。 我的问题是我的 Java 程序找不到我试图用作的图像文件一个 JButton。 (目前这段代码什么也没做,因为我只是得到了想要的外观第一的)。这是我的主课 代码: packag
好的,今天我在接受采访,我已经编写 Java 代码多年了。采访中说“Java 垃圾收集是一个棘手的问题,我有几个 friend 一直在努力弄清楚。你在这方面做得怎么样?”。她是想骗我吗?还是我的一生都
我的 friend 给了我一个谜语让我解开。它是这样的: There are 100 people. Each one of them, in his turn, does the following
如果我将使用 Java 5 代码的应用程序编译成字节码,生成的 .class 文件是否能够在 Java 1.4 下运行? 如果后者可以工作并且我正在尝试在我的 Java 1.4 应用程序中使用 Jav
有关于why Java doesn't support unsigned types的问题以及一些关于处理无符号类型的问题。我做了一些搜索,似乎 Scala 也不支持无符号数据类型。限制是Java和S
我只是想知道在一个 java 版本中生成的字节码是否可以在其他 java 版本上运行 最佳答案 通常,字节码无需修改即可在 较新 版本的 Java 上运行。它不会在旧版本上运行,除非您使用特殊参数 (
我有一个关于在命令提示符下执行 java 程序的基本问题。 在某些机器上我们需要指定 -cp 。 (类路径)同时执行java程序 (test为java文件名与.class文件存在于同一目录下) jav
我已经阅读 StackOverflow 有一段时间了,现在我才鼓起勇气提出问题。我今年 20 岁,目前在我的家乡(罗马尼亚克卢日-纳波卡)就读 IT 大学。足以介绍:D。 基本上,我有一家提供簿记应用
我有 public JSONObject parseXML(String xml) { JSONObject jsonObject = XML.toJSONObject(xml); r
我已经在 Java 中实现了带有动态类型的简单解释语言。不幸的是我遇到了以下问题。测试时如下代码: def main() { def ks = Map[[1, 2]].keySet()
一直提示输入 1 到 10 的数字 - 结果应将 st、rd、th 和 nd 添加到数字中。编写一个程序,提示用户输入 1 到 10 之间的任意整数,然后以序数形式显示该整数并附加后缀。 public
我有这个 DownloadFile.java 并按预期下载该文件: import java.io.*; import java.net.URL; public class DownloadFile {
我想在 GUI 上添加延迟。我放置了 2 个 for 循环,然后重新绘制了一个标签,但这 2 个 for 循环一个接一个地执行,并且标签被重新绘制到最后一个。 我能做什么? for(int i=0;
我正在对对象 Student 的列表项进行一些测试,但是我更喜欢在 java 类对象中创建硬编码列表,然后从那里提取数据,而不是连接到数据库并在结果集中选择记录。然而,自从我这样做以来已经很长时间了,
我知道对象创建分为三个部分: 声明 实例化 初始化 classA{} classB extends classA{} classA obj = new classB(1,1); 实例化 它必须使用
我有兴趣使用 GPRS 构建车辆跟踪系统。但是,我有一些问题要问以前做过此操作的人: GPRS 是最好的技术吗?人们意识到任何问题吗? 我计划使用 Java/Java EE - 有更好的技术吗? 如果
我可以通过递归方法反转数组,例如:数组={1,2,3,4,5} 数组结果={5,4,3,2,1}但我的结果是相同的数组,我不知道为什么,请帮助我。 public class Recursion { p
有这样的标准方式吗? 包括 Java源代码-测试代码- Ant 或 Maven联合单元持续集成(可能是巡航控制)ClearCase 版本控制工具部署到应用服务器 最后我希望有一个自动构建和集成环境。
我什至不知道这是否可能,我非常怀疑它是否可能,但如果可以,您能告诉我怎么做吗?我只是想知道如何从打印机打印一些文本。 有什么想法吗? 最佳答案 这里有更简单的事情。 import javax.swin
我是一名优秀的程序员,十分优秀!