キモブロ

Please spy check please, Fucking retard

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