Qiita Teams that are logged in
You are not logged in to any team

Log in to Qiita Team
Community
OrganizationAdvent CalendarQiitadon (β)
Service
Qiita JobsQiita ZineQiita Blog
Help us understand the problem. What is going on with this article?

TCP localhostとUnix Domain Socketはどちらが速いのか?

More than 5 years have passed since last update.

きっかけ

双方向のプロセス間通信をする必要が出てきたのですが、TCPのlocalhost接続とUNIX Domain Socketと、どっちの方がパフォーマンスがいいのか、実測してみました

検証内容

接続するとランダムな文字列を返すだけのサーバを作成
以下の接続方法について、10,000リクエストを同時接続 1/5/10 と条件を変えて計測

  1. TCP localhost
  2. Unix Domain Socket / Filesystem Namespace
  3. Unix Domain Socket / Abstract Namespace

結論

UNIX Domain Socketの方が速い

当環境においては19倍の違いが計測された

また、UNIX Domain Socketの名前空間の比較においては、極めて若干だがAbstract名前空間の方が速い

プロセス間通信においては、サーバプロセス終了時のファイル削除の考慮も不要で扱いやすく、速度も速いUnix Domain SocketのAbstract名前空間を使用するのが好ましいと言える

グラフ: https://docs.google.com/spreadsheets/d/1yLxWHZtYPAvJLu0NT3OXpBn4s3tugsxYFcKQjtb440w/pubhtml

考察

TCP側が遅いのは、ソケット操作処理が支配的なのだと考えられる

計測環境とログ

  • NEC LaViE G Type Z
    • Intel(R) Core(TM) i7-4500U CPU @ 1.80GHz
    • MemTotal: 3966364 kB
    • Disk: Model=SAMSUNG MZMTD256HAGM-000L1
  • Ubuntu 14.04
  • ruby 2.1.3p242
    • Celluloid-IO 0.16.1

ログ

$ bundle exec ruby client.rb 
       user     system      total        real
tcp__1  1.150000   0.490000   1.640000 (  1.751974)
ufs__1  0.210000   0.220000   0.430000 (  1.326359)
uab__1  0.230000   0.170000   0.400000 (  1.314750)
tcp__5 17.040000   8.420000  25.460000 ( 21.795437)
ufs__5  0.660000   0.550000   1.210000 (  4.083943)
uab__5  0.800000   0.460000   1.260000 (  4.174697)
tcp_10 31.830000  16.440000  48.270000 ( 41.255494)
ufs_10  1.490000   1.040000   2.530000 (  8.242772)
uab_10  1.600000   0.890000   2.490000 (  8.438751)
$ bundle exec ruby client.rb 
       user     system      total        real
tcp__1  1.270000   0.440000   1.710000 (  1.823257)
ufs__1  0.360000   0.180000   0.540000 (  1.650133)
uab__1  0.210000   0.110000   0.320000 (  1.112440)
tcp__5 16.390000   8.130000  24.520000 ( 21.806182)
ufs__5  0.820000   0.610000   1.430000 (  4.612291)
uab__5  0.820000   0.560000   1.380000 (  4.604220)
tcp_10 32.470000  16.470000  48.940000 ( 41.773062)
ufs_10  1.500000   1.030000   2.530000 (  8.260133)
uab_10  1.460000   0.970000   2.430000 (  8.183293)
$ bundle exec ruby client.rb 
       user     system      total        real
tcp__1  1.240000   0.490000   1.730000 (  1.854051)
ufs__1  0.230000   0.170000   0.400000 (  1.298220)
uab__1  0.160000   0.200000   0.360000 (  1.197231)
tcp__5 16.720000   8.260000  24.980000 ( 22.068534)
ufs__5  0.870000   0.600000   1.470000 (  4.631703)
uab__5  0.830000   0.510000   1.340000 (  4.425737)
tcp_10 32.260000  16.220000  48.480000 ( 41.639263)
ufs_10  1.600000   0.950000   2.550000 (  8.327180)
uab_10  1.540000   1.010000   2.550000 (  8.519080)

検証コード

Gemfile
source "https://rubygems.org"
gem "celluloid-io", :require => "celluloid/io"
gem "parallel"
server.rb
require 'bundler'
Bundler.require(:default)

class BaseServer
  include Celluloid::IO
  finalizer :finalize

  def initialize
    raise "abstract"
  end

  def finalize
    @server.close if @server
  end

  def run
    loop { async.handle_connection @server.accept }
  end

  def handle_connection(socket)
    socket.write rand.to_s
  rescue EOFError
    nil
  ensure
    socket.close
  end
end

class UServer < BaseServer
  attr_reader :socket_path, :server

  def initialize(socket_path)
    STDERR.puts "** UnixServer start"
    @socket_path = socket_path
    @server = UNIXServer.new(@socket_path)
    async.run
  end

  def finalize
    super
    File.delete(@socket_path) if File.exists?(@socket_path)
  end
end

class TServer < BaseServer
  attr_reader :server

  def initialize(opts)
    STDERR.puts "** TCPServer start"
    @server = TCPServer.new(opts[:host], opts[:port])
    async.run
  end
end

unix_fs = UServer.supervise("./socket")
unix_ab = UServer.supervise("\0socket")
tcpl = TServer.supervise(:host => "127.0.0.1", :port => 1234)
trap("INT") {
  unix_fs.terminate
  unix_ab.terminate
  tcpl.terminate
  exit
}
STDERR.puts "STOP is CTRL+C"
sleep
client.rb
require 'bundler'
Bundler.require(:default)
require "benchmark"

class ClientBase
  attr_reader :socket, :opts

  def run(loop_counter, mark)
    i = 0
    while (i < loop_counter) do
      @socket.open(*@opts) do |s|
        _ = "#{mark} > #{s.readpartial(4096)}"
      end
      i += 1
    end
  end
end

class TClient < ClientBase
  def initialize(opts)
    @opts = [opts[:host], opts[:port]]
    @socket = Celluloid::IO::TCPSocket
  end
end

class UClient < ClientBase
  def initialize(opts)
    @opts = [opts]
    @socket = Celluloid::IO::UNIXSocket
  end
end

tcp= TClient.new(:host => "127.0.0.1", :port => 1234)
ufs = UClient.new("./socket")
uab = UClient.new("\0socket")

reqs = 10_000

Benchmark.bm {|x|
  x.report(:tcp__1) { Parallel.each(1..1, :in_threads => 1) do |i| tcp.run(reqs, i) ; end }
  x.report(:ufs__1) { Parallel.each(1..1, :in_threads => 1) do |i| ufs.run(reqs, i) ; end }
  x.report(:uab__1) { Parallel.each(1..1, :in_threads => 1) do |i| uab.run(reqs, i) ; end }
  x.report(:tcp__5) { Parallel.each(1..5, :in_threads => 5) do |i| tcp.run(reqs, i) ; end }
  x.report(:ufs__5) { Parallel.each(1..5, :in_threads => 5) do |i| ufs.run(reqs, i) ; end }
  x.report(:uab__5) { Parallel.each(1..5, :in_threads => 5) do |i| uab.run(reqs, i) ; end }
  x.report(:tcp_10) { Parallel.each(1..10, :in_threads => 10) do |i| tcp.run(reqs, i) ; end }
  x.report(:ufs_10) { Parallel.each(1..10, :in_threads => 10) do |i| ufs.run(reqs, i) ; end }
  x.report(:uab_10) { Parallel.each(1..10, :in_threads => 10) do |i| uab.run(reqs, i) ; end }
}

あとがき

そんな気はしてたよ。

ma2shita
(松下 享平) IoT通信プラットフォーム「ソラコム」のエバンジェリストで年間140回以上の講演を通じ #IoT の事例や技術情報を日々紹介。LPWAの選び方や共著で「公式ガイドブック SORACOM プラットフォーム」を書いています。2020年の #AWS IoT Heroです。"Max"はニックネームです、フォローやメッセージはお気軽に! Twitterは @ma2shita です。
iotlt
IoT縛りの勉強会です。 毎月イベントを実施しているので是非遊びに来てください! 登壇者を中心にQiitaでも情報発信していきます。 https://iotlt.connpass.com
https://iotlt.connpass.com/
Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away