2015-08-08 39 views
0

我目前有一個遊戲,爲此我實現了一個客戶端和一個服務器。CPU-Wise,如何優化UDP數據包發送?

我然後讓服務器向客戶端關於它的位置發送數據,客戶端發送移動輸入到服務器等

的問題是,在CPU焰火至100%。我已經直接連接在高使用率下面的代碼,這是在被每秒稱爲十倍的更新()方法:

try{ 
     sendToClientUDP(("ID:" + String.valueOf(uid))); 
     sendToClientUDP(("Scale:" + GameServer.scale)); 

     for (Clients cl : GameServer.players){ 
      //sendToClient(("newShip;ID:" + cl.uid).getBytes(), packet.getAddress(), packet.getPort()); 
      sendToClientUDP((("UID:" + cl.uid +";x:" + cl.x))); 
      sendToClientUDP((("UID:" + cl.uid +";y:" + cl.y))); 
      sendToClientUDP((("UID:" + cl.uid +";z:" + cl.z))); 
      sendToClientUDP((("UID:" + cl.uid +";Rotation:" + (cl.rotation)))); 
      cl.sendToClientUDP(new String("newShip;ID:" + uid)); 
      sendToClientUDP(new String("newShip;ID:" + cl.uid)); 
     } 
     }catch (Exception e){ 
      e.printStackTrace(); 
     } 

卸下代碼和CPU使用率過高消失。

這是我的sendToClientUDP()方法。

public void sendToClientUDP(String str){ 
     if (!NPC){ //NPC is checking if it is a computer-controlled player. 
     UDP.sendData(str.getBytes(), ip, port); 
     } 
    } 

這裏是我的UDP.sendData()方法:

public static void sendData(String data, InetAddress ip, int port) { 
    sendData(data.getBytes(), ip, port); 
} 

public static void sendData(byte[] data, InetAddress ip, int port) { 
    DatagramPacket packet = new DatagramPacket(data, data.length, ip, port); 
    try { 
     socket.send(packet); 
    } catch (IOException e) { 
     e.printStackTrace(); 
    } 
} 

爲什麼如此多的CPU被簡單地通過發送UDP數據包使用?如果有的話,我能做些什麼來減少它?

+0

錯誤,發送所有的東西在一個單一的數據報? – EJP

回答

2

我建議你拿出或優化產生這麼多CPU的代碼,CPU分析器是最好的啓動地點,但這些可能是CPU消耗的原因。

  • 創建字符串和字節[]是昂貴的,我會避免做那些。
  • 創建多個數據包而不是批處理它們也很昂貴。
  • 可以避免創建一個新的DatagramPacket。
  • 我會消除消息之間的重複,因爲這會增加您可以避免的冗餘工作。
  • 您可能會考慮使用二進制格式來避免轉換爲文本/從文本轉換的開銷。
  • 幾乎沒有好時間使用new String()它幾乎肯定是多餘的。

編輯:這是我的想法。不是每個客戶端發送5個數據包,總共只發送一個數據包。對於十個客戶端,你發送1/50的數據包,減少開銷。

import java.io.IOException; 
import java.net.*; 
import java.nio.ByteBuffer; 
import java.nio.ByteOrder; 
import java.util.ArrayList; 
import java.util.List; 

/** 
* Created by peter on 31/07/15. 
*/ 
public class PacketSender { 
    public static void main(String[] args) throws IOException { 
     PacketSender ps = new PacketSender(InetAddress.getByName("localhost"), 12345); 
     List<Client> clients = new ArrayList<>(); 
     for(int i=0;i<10;i++) 
      clients.add(new Client()); 

     for(int t = 0; t< 3;t++) { 
      long start = System.nanoTime(); 
      int tests = 100000; 
      for (int i = 0; i < tests; i++) { 
       ps.sendData(1234, 1, clients); 
      } 
      long time = System.nanoTime() - start; 
      System.out.printf("Sent %,d messages per second%n", (long) (tests * 1e9/time)); 
     } 
    } 


    final ThreadLocal<ByteBuffer> bufferTL = ThreadLocal.withInitial(() -> ByteBuffer.allocate(8192).order(ByteOrder.nativeOrder())); 
    final ThreadLocal<DatagramSocket> socketTL; 
    final ThreadLocal<DatagramPacket> packetTL; 

    public PacketSender(InetAddress address, int port) { 
     socketTL = ThreadLocal.withInitial(() -> { 
      try { 
       return new DatagramSocket(port, address); 
      } catch (SocketException e) { 
       throw new AssertionError(e); 
      } 
     }); 
     packetTL = ThreadLocal.withInitial(() -> new DatagramPacket(bufferTL.get().array(), 0, address, port)); 
    } 

    public void sendData(int uid, int scale, List<Client> clients) throws IOException { 
     ByteBuffer b = bufferTL.get(); 
     b.clear(); 
     b.putInt(uid); 
     b.putInt(scale); 
     b.putInt(clients.size()); 
     for (Client cl : clients) { 
      b.putInt(cl.x); 
      b.putInt(cl.y); 
      b.putInt(cl.z); 
      b.putInt(cl.rotation); 
      b.putInt(cl.uid); 
     } 
     DatagramPacket dp = packetTL.get(); 
     dp.setData(b.array(), 0, b.position()); 
     socketTL.get().send(dp); 
    } 

    static class Client { 
     int x,y,z,rotation,uid; 
    } 
} 

當這個性能測試運行它打印

Sent 410,118 messages per second 
Sent 458,126 messages per second 
Sent 459,499 messages per second 

編輯:寫/讀課文,你可以做到以下幾點。

import java.nio.ByteBuffer; 

/** 
* Created by peter on 09/08/2015. 
*/ 
public enum ByteBuffers { 
    ; 
    /** 
    * Writes in ISO-8859-1 encoding. This assumes string up to 127 bytes long. 
    * 
    * @param bb to write to 
    * @param cs to write from 
    */ 
    public static void writeText(ByteBuffer bb, CharSequence cs) { 
     // change to stop bit encoding to have lengths > 127 
     assert cs.length() < 128; 
     bb.put((byte) cs.length()); 
     for (int i = 0, len = cs.length(); i < len; i++) 
      bb.put((byte) cs.charAt(i)); 
    } 

    public static StringBuilder readText(ByteBuffer bb, StringBuilder sb) { 
     int len = bb.get(); 
     assert len >= 0; 
     sb.setLength(0); 
     for (int i = 0; i < len; i++) 
      sb.append((char) (bb.get() & 0xFF)); 
     return sb; 
    } 

    private static final ThreadLocal<StringBuilder> SB = new ThreadLocal<>() { 
     @Override 
     protected Object initialValue() { 
      return new StringBuilder(); 
     } 
    }; 

    public static String readText(ByteBuffer bb) { 
     // TODO use a string pool to reduce String garbage. 
     return readText(bb, SB.get()).toString(); 
    } 
} 

如果你需要更復雜的東西,你應該考慮使用Chronicle-Bytes這是我寫的。它有

  • 支持64位內存大小,包括內存映射64位。
  • 線程安全操作堆外。
  • 字符串的UTF-8編碼。
  • 壓縮類型,如停止位編碼。
  • 自動字符串池化減少垃圾。
  • 確定性通過引用計數清理掉堆資源。
+0

這就是我要問你的。我已經嘗試拿出解決問題的代碼,但它對遊戲至關重要,並且無法取出。創建多個數據包的確很昂貴,但肯定不會達到每秒100個數據包等於100%CPU的程度?如果真的如此,* ANY *公共遊戲如何在實時環境中使用CPU? – Joehot200

+0

我剛看到你的編輯。我怎樣才能避免創建一個DatagramPacket?另外,我怎麼不能創建一個字符串或字節[]? – Joehot200

+0

@ Joehot200沒有分析你的應用程序,你只能猜測它在做什麼,但我懷疑你的字符串創建和getBytes()實際上比發送這麼多的數據包更昂貴。順便說一句,你爲什麼不每次只看一個? –