【发布时间】:2012-02-05 18:56:33
【问题描述】:
我最近读到,由于多核机器的新可用性,Java 的 I/O 性能在较新的计算机中优于 NIO。
我使用 localhost 环回地址运行了一个快速测试,比较了 LAN 上 I/O 和 NIO 的传输时间。
注意:这里使用的是 JDK 7
结果(3次试验):
平均 I/O 传输 21789.3ms
NIO 传输平均 22771.0ms
还值得注意的是,与 I/O 相比,每次 NIO 传输的 CPU 使用率似乎高出约 10%。
我的问题是我的比较代码是否公平?我是否编写了良好/相等的 I/O 和 NIO 代码?如果没有,我该如何改进并重新运行此测试?
public static void main(String[] args) {
System.out.println("Initiating test sequence...");
new Thread(new Client()).start();
try {
System.out.println("Server I/O initiating...");
ServerSocket server = new ServerSocket(5555);
Socket sock = server.accept();
System.out.println("Server connected to client successfully");
InputStream is = sock.getInputStream();
File output = new File("C:/test_root/video.avi");
FileOutputStream fos = new FileOutputStream(output);
byte[] data = new byte[1024];
int len=0;
System.out.println("Server initiating transfer - Timer starting");
long start = System.currentTimeMillis();
while((len=is.read(data))>0) {
fos.write(data, 0, len);
fos.flush();
}
fos.close();
is.close();
sock.close();
server.close();
long end = System.currentTimeMillis();
System.out.println("Network I/O transfer time = "+(end-start)+"ms");
System.out.println("Server NIO initiating...");
ServerSocketChannel serverChan = ServerSocketChannel.open();
serverChan.bind(new InetSocketAddress(5555));
SocketChannel chan = serverChan.accept();
chan.configureBlocking(false);
System.out.println("Server channel connected");
FileChannel fc = (FileChannel) Files.newByteChannel(Paths.get("C:/test_root/video.avi"), StandardOpenOption.CREATE, StandardOpenOption.WRITE);
ByteBuffer buff = ByteBuffer.allocate(1024);
System.out.println("Server initiating transfer - Timer starting");
start = System.currentTimeMillis();
while(chan.read(buff)>=0 || buff.position() > 0) {
buff.flip();
fc.write(buff);
buff.compact();
}
chan.close();
fc.close();
serverChan.close();
end = System.currentTimeMillis();
System.out.println("Network NIO transfer time = "+(end-start)+"ms");
} catch (IOException e) {
e.printStackTrace();
}
System.out.println("Test completed!");
}
static class Client implements Runnable {
public void run() {
try {
System.out.println("Client I/O initiating...");
Socket sock = new Socket("localhost", 5555);
System.out.println("Client connected to server successfully!");
OutputStream os = sock.getOutputStream();
File input = new File(System.getProperty("user.home")+"/Documents/clip0025.avi");
FileInputStream fis = new FileInputStream(input);
byte[] data = new byte[1024];
int len=0;
int tot=0;
int perc=0;
while((len=fis.read(data))>0) {
os.write(data, 0, len);
os.flush();
tot+=len;
int prev = perc;
perc = getPercentage(tot, input.length());
if(perc !=prev && (perc == 10 || perc == 25 || perc == 50 || perc == 75 || perc == 98))
System.out.println("Client reporting: "+perc+"% read");
}
os.close();
fis.close();
sock.close();
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("Client NIO initiating...");
SocketChannel sc = SocketChannel.open();
boolean connected = sc.connect(new InetSocketAddress("localhost",5555));
if(!connected)
connected = sc.finishConnect();
if(!connected)
throw(new IOException("Client failed to connect"));
System.out.println("Client channel connected");
sc.configureBlocking(false);
FileChannel fc = (FileChannel) Files.newByteChannel(input.toPath(), StandardOpenOption.READ);
ByteBuffer buff = ByteBuffer.allocate(1024);
len=0;
tot=0;
while((len=fc.read(buff))>=0||buff.position()>0) {
buff.flip();
sc.write(buff);
buff.compact();
tot+=len;
int prev = perc;
perc = getPercentage(tot, input.length());
if(perc !=prev && (perc == 10 || perc == 25 || perc == 50 || perc == 75 || perc == 98))
System.out.println("Client reporting: "+perc+"% read");
}
sc.close();
fc.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
附加信息:
Dell Studio XPS 435MT 上的 Windows Vista (SP2)
第一代 i7 四核处理器 2.67GHz
6GB 内存
64 位架构
【问题讨论】:
-
我不相信基准测试部分是公平的——在 Java 中,你确实需要做一些事情,比如预热 JVM 等等。您也许可以使用像 code.google.com/p/caliper 这样的基准测试工具。
-
@LouisWasserman 您如何建议预热 JVM 和/或改进此代码?
-
使用由知道自己在 JVM 上做什么的人构建的基准测试框架,本质上。不要编写自己的“定时操作”代码。错误的方法比正确的方法多得多。
-
您同时处理多少个连接? Java NIO 的主要设计目的是避免“每个客户端一个线程”的方法。人们会认为真正的 NIO 基准测试将涉及大量客户端冲击同一个服务器实例。
-
NIO 的重点是解决传统 IO 的可扩展性问题,当尝试 5k 多个客户端时。这个基准完全没有意义。
标签: java networking io benchmarking nio