中文字幕av专区_日韩电影在线播放_精品国产精品久久一区免费式_av在线免费观看网站

溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務條款》

NIO與零拷貝的示例分析

發布時間:2022-01-15 18:04:53 來源:億速云 閱讀:100 作者:柒染 欄目:大數據

這期內容當中小編將會給大家帶來有關NIO與零拷貝的示例分析,文章內容豐富且以專業的角度為大家分析和敘述,閱讀完這篇文章希望大家可以有所收獲。

 

一、NIO是什么

先來看如下一段代碼:

File file = new File("test.txt");
RandomAccessFile raf = new RandomAccessFile(file, "rw");
  
byte[] arr = new byte[(int)file.length()];
raf.read(arr);
  
Socket socket = new ServerSocket(8888).accept();
socket.getOutputStream().write(arr);
 

這段代碼就是讀取一個文件,然后再把它寫出去,看起來就幾行代碼,其實涉及到多次拷貝,其流程如下:

  • 讀取需要拷貝的數據,這個過程有兩個步驟:首先將文件通過DMA(direct memory access,直接內存拷貝,不經過CPU) 拷貝到系統內核的buffer中,然后在內核buffer中通過 CPU拷貝到用戶的buffer中,這才完成了讀取文件的步驟。即前四行就發生了兩次拷貝。

  • 寫數據的時候,將數據從用戶buffer通過CPU拷貝到socket buffer中,最后從socket buffer通過DMA拷貝到協議棧。即最后一行也發生了兩次拷貝。

整個過程,發生了四次拷貝,三次狀態的切換。從一開始的用戶態,切換到內核態,再切換到用戶態,最后再切換成內核態。一次簡單的讀寫,就有這么多名堂,性能肯定是不好的,所有就出現了零拷貝,零拷貝,不是不拷貝,而是整個過程不需要進行CPU拷貝。

 

二、零拷貝

1、使用mmap優化上述流程:mmap,是指通過內存映射,將文件映射到內核緩沖區,同時,用戶空間可以共享內核空間的數據,這樣,在進行網絡傳輸時,就可以減少內核空間到用戶空間的拷貝次數。同樣做上面的事情,使用mmap時整個過程如下:

  • 首先通過DMA拷貝將硬盤數據拷貝到內核buffer,但是因為用戶buffer可以共享內核buffer的數據,所以步驟二的cpu拷貝就免了;

  • 然后是直接從內核buffer通過CPU拷貝到socket buffer,最后DMA拷貝到協議棧。

整個過程三次拷貝,三次狀態的切換,相比傳統拷貝,優化了一丟丟,但這并不是零拷貝。

2、使用sendFile優化:linux 2.1的sendFile:sendFile是linux2.1版本開始提供的一個函數,可以讓文件直接從內核buffer進入到socket buffer,不需要經過用戶態,過程如下:

  • 首先還是將數據從硬盤中通過DMA拷貝到內核buffer,然后通過CPU拷貝將數據從內核buffer拷貝到socket buffer,最后通過DMA拷貝到協議棧。

整個過程還是3次拷貝,但是減少了一次裝態切換,從用戶態到內核態再到用戶態,只經過了兩次切換。這里還是有一次CPU拷貝,還不是真正的零拷貝。

linux 2.4的sendFile:linux 2.4對sendFile又做了一些優化,首先還是DMA拷貝到內核buffer,然后再通過CPU拷貝到socket buffer,最后DMA拷貝到協議棧。優化的點就在于,這次的CPU拷貝,拷貝的內容很少,只拷貝內核buffer的長度、偏移量等信息,消耗很低,可以忽略。因此,這個就是零拷貝。NIO的transferTo方法就可以實現零拷貝。

 

三、案例代碼

1、傳統IO拷貝大文件:

  • 服務端:接收客戶端發來的數據
public class OldIoServer {
 
 @SuppressWarnings("resource")
 public static void main(String[] args) throws IOException {
  ServerSocket serverSocket = new ServerSocket(6666);
  while (true) {
   Socket socket = serverSocket.accept();
   DataInputStream dataInputStream = new DataInputStream(socket.getInputStream());
   byte[] byteArray = new byte[4096];
   while (true) {
    int readCount = dataInputStream.read(byteArray, 0, byteArray.length);
    if (-1 == readCount) {
     break;
    }
   }
  }
 }
}
 
  • 客戶端:發送一個大文件到服務端:
public class OldIoClient {
 
 @SuppressWarnings("resource")
 public static void main(String[] args) throws Exception {
  Socket socket = new Socket("127.0.0.1", 6666);
  // 需要拷貝的文件
  String fileName = "E:\\download\\soft\\windows\\jdk-8u171-windows-x64.exe";
  InputStream inputStream = new FileInputStream(fileName);
  DataOutputStream dataOutputStream = new DataOutputStream(socket.getOutputStream());
  byte[] buffer = new byte[4096];
  long readCount;
  long total = 0;
  long start = System.currentTimeMillis();
  while ((readCount = inputStream.read(buffer)) >= 0) {
   total += readCount;
   dataOutputStream.write(buffer);
  }
  long end = System.currentTimeMillis();
  System.out.println("傳輸總字節數:" + total + ",耗時:" + (end - start) + "毫秒");
  dataOutputStream.close();
  inputStream.close();
  socket.close();
 }
}
 

這里拷貝了一個JDK,最后運行結果如下:

傳輸總字節數:217342912,耗時:4803毫秒
 

可以看到,將近5秒鐘。接下來看看使用NIO的transferTo方法耗時情況:

  • 服務端:
public class NioServer {

 public static void main(String[] args) throws IOException {
  InetSocketAddress address = new InetSocketAddress(6666);
  ServerSocketChannel serverSocketChannel = ServerSocketChannel.open();
  ServerSocket serverSocket = serverSocketChannel.socket();
  serverSocket.bind(address);
  ByteBuffer buffer = ByteBuffer.allocate(4096);
  while (true) {
   SocketChannel socketChannel = serverSocketChannel.accept();
   int readCount = 0;
   while (-1 != readCount) {
    readCount = socketChannel.read(buffer);
    buffer.rewind(); // 倒帶,將position設置為0,mark設置為-1
   }
  }
 }
}
 
  • 客戶端:
public class NioClient {
 
 @SuppressWarnings("resource")
 public static void main(String[] args) throws IOException {
  SocketChannel socketChannel = SocketChannel.open();
  socketChannel.connect(new InetSocketAddress("127.0.0.1", 6666));
  String fileName = "E:\\download\\soft\\windows\\jdk-8u171-windows-x64.exe";
  FileChannel channel = new FileInputStream(fileName).getChannel();
  long start = System.currentTimeMillis();
  // 在linux下,transferTo方法可以一次性發送數據
        // 在windows中,transferTo方法傳輸的文件超過8M得分段
  long totalSize = channel.size();
  long transferTotal = 0;
  long position = 0;
  long count = 8 * 1024 * 1024;
  if (totalSize > count) {
   BigDecimal totalCount = new BigDecimal(totalSize).divide(new BigDecimal(count)).setScale(0, RoundingMode.UP);
   for (int i=1; i<=totalCount.intValue(); i++) {
    if (i == totalCount.intValue()) {
     transferTotal += channel.transferTo(position, totalSize, socketChannel);
    } else {
     transferTotal += channel.transferTo(position, count + position, socketChannel);
     position = position + count;
    }
   }
  } else {
   transferTotal += channel.transferTo(position, totalSize, socketChannel);
  }
  
  long end = System.currentTimeMillis();
  System.out.println("發送的總字節:" + transferTotal + ",耗時:" + (end - start) + "毫秒");
  channel.close();
  socketChannel.close();
 }
}
 

客戶端發送文件調用transferTo方法要注意,在window中,這個方法一次只能傳輸8M,超過8M的文件要分段,像代碼中那樣分段傳輸,在linux中是沒這個限制的。運行后結果如下:

發送的總字節:217342912,耗時:415毫秒
 

從結果可以看到,BIO與NIO耗時相差一個數量級,NIO只要0.4s,而BIO要4s。所以在網絡傳輸中,使用NIO的零拷貝,可以大大提高性能。

上述就是小編為大家分享的NIO與零拷貝的示例分析了,如果剛好有類似的疑惑,不妨參照上述分析進行理解。如果想知道更多相關知識,歡迎關注億速云行業資訊頻道。

向AI問一下細節

免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。

nio
AI

阿坝县| 磴口县| 邢台县| 连平县| 阳泉市| 房山区| 武川县| 保德县| 承德县| 吕梁市| 乡宁县| 建湖县| 湘潭县| 芜湖县| 赣榆县| 岳普湖县| 云龙县| 儋州市| 哈尔滨市| 沧源| 太康县| 邳州市| 房产| 安溪县| 宁强县| 山东| 潍坊市| 平昌县| 静安区| 阜阳市| 会泽县| 明星| 博野县| 塔河县| 莒南县| 阿瓦提县| 肥乡县| 永春县| 沙田区| 许昌县| 泰州市|