2009-07-27 5 views
0

Мне нужно ускорить передачу через соединение с гигабитным Ethernet. Прямо сейчас, я делаю что-то почти такое, но я вижу только около 40% этого, когда я запускаю этот код ниже.Ускорьте передачу Java tcp!

Я побежал этот сценарий на всех моих (Pro Mac) машин перед тестированием

#!/bin/bash 

sudo sysctl -w net.inet.tcp.win_scale_factor=8 
sudo sysctl -w kern.ipc.maxsockbuf=16777216 
sudo sysctl -w net.inet.tcp.sendspace=8388608 
sudo sysctl -w net.inet.tcp.recvspace=8388608 

Фактический код следующим образом:

import java.io.*; 
import java.nio.*; 
import java.net.*; 

public class BandwidthTester { 
private static final int OUT_BUF = (1 << 17), 
        IN_BUF = (1 << 17), SEND_BUF = (1 << 22), RECV_BUF = (1 << 22); 
public static void main(String[] args) { 
    try { 
     // server 
     if (args.length == 0) { 
      ServerSocket sock = new ServerSocket(); 
      sock.bind(new InetSocketAddress(41887)); 

      // wait for connection 
      Socket s = sock.accept(); 

      s.setSendBufferSize(SEND_BUF); 

      System.out.println("Buffers: " + s.getSendBufferSize() + " and " + s.getReceiveBufferSize()); 

      sock.close(); 

      BufferedOutputStream bOut = new BufferedOutputStream(s.getOutputStream(), OUT_BUF); 

      // send lots of data 
      sendLotsOfData(bOut); 
     } else if (args.length == 2) { 
      String host = args[0]; 
      int port = Integer.parseInt(args[1]); 

      System.out.println("Connecting to " + args[0] + ":" + args[1]); 

      Socket sock = new Socket(); 
      sock.setReceiveBufferSize(RECV_BUF); 
      sock.connect(new InetSocketAddress(host, port)); 

      System.out.println("Buffers: " + sock.getSendBufferSize() + " and " + sock.getReceiveBufferSize()); 

      BufferedInputStream bIn = new BufferedInputStream(sock.getInputStream(), IN_BUF); 
      getLotsOfData(bIn); 
     } 
    } catch (Exception e) { 
     e.printStackTrace(); 
    } 
} 


public static void getLotsOfData(InputStream in) { 
    System.out.println("Getting data..."); 
    try { 
     long start = System.currentTimeMillis(); 

     ByteBuffer intConv = ByteBuffer.allocate(4); 

     in.read(intConv.array()); 
     int len = intConv.getInt(0); 
     for (int i=0; i < len; i++) { 
      in.read(intConv.array()); 
      int val = intConv.getInt(0); 
     } 

     long end = System.currentTimeMillis(); 

     double elapsed = ((double)(end - start))/(1000.0); 

     System.out.println("Read in " + elapsed + " seconds: " + ((4.0*8.0*len/elapsed) + " bits per second")); 
    } catch (Exception e) { 
     e.printStackTrace(); 
    } 
} 

public static void sendLotsOfData(OutputStream out) { 
    System.out.println("Sending data..."); 
    try { 
     long start = System.currentTimeMillis(); 

     int len = (1 << 29); 

     ByteBuffer intConv = ByteBuffer.allocate(4); 
     intConv.putInt(0, len); 
     out.write(intConv.array()); 
     for (int i=0; i < len; i++) { 
      intConv.putInt(0, i); 
      out.write(intConv.array()); 
     } 

     out.flush(); 

     long end = System.currentTimeMillis(); 

     double elapsed = ((double)(end - start))/(1000.0); 

     System.out.println("Sent in " + elapsed + " seconds: " + ((4.0*8.0*len/elapsed) + " bits per second")); 
    } 
    catch (Exception e) { 
     e.printStackTrace(); 
    } 
} 
} 

Любые предложения? Для отправки всех этих данных требуется около 42 секунд, но даже 10% -ное улучшение здесь оказало бы значительное влияние на мою программу.

ответ

1

Одна вещь, которую вы можете попробовать, - это использовать большой буфер для ByteBuffer. Начиная с 4 байт до 16, я перешел с 12-секундного времени передачи на 9-секундное время передачи. (проверено с использованием 2^26, а не 2^29 для длины)

Это говорит о том, что он запускался локально; поэтому никаких реальных проблем с сетью не должно возникать.

Несколько грязный модифицированный код для отправки:

ByteBuffer intConv = ByteBuffer.allocate(16); 
intConv.putInt(0, len); 
out.write(intConv.array(),0,4); 
for (int i=0; i < len; i+=4) { 
    for(int j=0; j<4; j++) 
     intConv.putInt(4*j, i); 
    out.write(intConv.array()); 
} 

И Принимающий:

ByteBuffer intConv = ByteBuffer.allocate(16); 
in.read(intConv.array(),0,4); 
int len = intConv.getInt(0); 
for (int i=0; i < len; i+=4) { 
    in.read(intConv.array()); 
    for(int j=0; j<4; j++) 
    { 
     int val=intConv.getInt(j*4); 
    } 
} 

Очевидно, что приемный конец потребуется некоторая модификация для обработки странных и необычных случаев, как «что если бы было только 3 ints остается/читается из потока », но я думаю, этого было бы достаточно, чтобы убедиться, что это повышает производительность.

+0

правый сверху. чем больше буфер, тем ближе к 1 Гбит/с. Я все еще запутался, как работает BufferedInputStream или BufferedOutputStream. Почему буфера сокета -> buffer -> будет намного быстрее, чем сокет -> буфер? – user143879

+0

Я бы заподозрил, что это своего рода замедление в том, что вы собираетесь читать/писать буферизованный поток столько раз; в то время как это быстрее, чем попытка чтения/записи нормального потока, вы также получаете расходы на работу с буфером. Впрочем, это только безумная спекуляция. – CoderTao

Смежные вопросы