UDPのスループットを計測した
リアルタイムに通信をしていろいろおもしろいことをするものを作ろうと思い、UDPだとどの程度スループットが出るか知りたかったので計測してみた。
しかし、UDPってパケットの順列も届いたかどうか自体もクライアントからはわからんから、負荷試験するの結構たいへんですね。。最初クライアントで処理にかかった時間を計測していて、超スループットでんじゃねーかと思って喜んでたけど、それを思い出して、あわててサーバー側で最初のパケット受信時間と最後に受信したパケットの受信時間の差を計測をするようにしたっていうね。
まずMacBook内で完結したテスト
UDP: Client(localhost) → Server(0.0.0.0)
良好な通信環境では50000req/sec近くでるのか。パケットもほとんどロスしていない。全体の0.8%のみロスト。
all packet: 100000, lost packets: 805, 0.8049999999999999% lost Total packet: 50411.17877971676 req/sec Success packet: 50005.368790540044 req/sec
次にアドレスの指定の仕方を変えてみた。0.0.0.0でServer側でbindしていたところを127.0.0.1にした。OS側の処理の分岐が減って早くなるんじゃないかなぁとか気になったので。
UDP:Client(localhost) → Server(127.0.0.1)
微妙に早くなってる気がする
all packet: 100000, lost packets: 763, 0.763% lost Total packet: 51525.51595075397 req/sec Success packet: 51132.37626404972 req/sec
次は、調子に乗ってどっちも127.0.0.1にしてみた。
UDP:Client(127.0.0.1) → Server(127.0.0.1)
all packet: 100000, lost packets: 1044, 1.044% lost Total packet: 51857.29494718075 req/sec Success packet: 51315.90478793219 req/sec
これは上と変わらないな
さくらVPS上でやってみる
次にさくらVPS上で同じ事をやってみた。さくらVPS上でローカルホストにサーバーを建てて、ローカルホストのサーバーに向かってアクセス。普通に考えたら同じ程度(0.8%)のロスしかしないはずなんだけど、というのが予想だった。
UDP: さくらVPSのClient(127.0.0.1) → さくらVPSのServer(127.0.0.1)
all packet: 100000, lost packets: 61072, 61.072% lost Total packet: 67726.52616330599 req/sec Success packet: 26364.582104851754 req/sec
超ロスしてる!! 60%。why? 同一サーバーでNICのインターフェースも明らかにループバックなのにかなりロスした。なんらかの帯域制限かな?
あるいは仮想環境の制限か何かなのかも。さくらVPSでSRCDSを動かすと妙にラグいのってこういう理由なんじゃないのかしら。
会社PC → さくらVPSでこれをやる
UDP: Client(会社PC) → さくらVPSのServer(0.0.0.0)
all packet: 100000, lost packets: 73595, 73.595% lost Total packet: 72543.29070202081 req/sec Success packet: 19155.055909868595 req/sec
まぁさくらVPS内でlocalhost自身でやってもロストしてるので当然同じ程度はロストしますよね。しかし10%しか変わらないな。成功パケット数のスループットは目に見えて遅くなった。
コード
以下検証に作ったコード
udp_server.rb
#!/bin/env ruby # encoding: utf-8 # Author: kimoto require 'socket' us = UDPSocket.open() us.bind("0.0.0.0", 7777) # 受け取れるべきパケット数 #requests = 100000 requests = 100000 # initialize received data hash received = {} requests.times{ |n| received[n.to_s] = nil } start = nil stop = nil # Ctrl-C trap # ちゃんとすべてのデータを受け取れてるか表示する # nil = 何も受け取れてない識別子のパケットなのでそれ表示 trap(:INT){ puts "Pressed Ctrl-C!!!" missed = [] received.each{ |k,v| if v.nil? missed << k end } percent = (missed.size.to_f / requests.to_f) * 100 puts "all packet: #{requests}, lost packets: #{missed.size}, #{percent}% lost" puts "Total packet: #{(requests.to_f / (stop - start).to_f)} req/sec" puts "Success packet: #{((requests - missed.size).to_f / (stop - start).to_f)} req/sec" exit 0 } loop do data = us.recv(32) if start.nil? start = Time.now end puts data if data == "END" puts "last packet received" end received[data] = true stop = Time.now end
udp_client.rb
#!/bin/env ruby # encoding: utf-8 # Author: kimoto require 'socket' udp = UDPSocket.open sockaddr = Socket.pack_sockaddr_in(7777, "kymt.me") requests = 100000 start = Time.now requests.times do |n| data = n.to_s udp.send(data, 0, sockaddr) puts data end udp.send("END", 0, sockaddr) puts "END" stop = Time.now diff = (stop - start) puts "#{(requests / diff)} req/sec" udp.close