2017-10-16 38 views
1

Erlangのhttpcを使用してHTTP経由でファイルをダウンロードすると、CPU使用率がcurlやwgetなどよりもはるかに高くなります。ダウンロード速度を測定するために使用しているコードは、この記事の末尾に表示されています。ファイルをダウンロードするときのCPU使用率が高い

CPU使用率が高いのは、特にローエンドのデバイスで問題になります。私はErlangを最初のRaspberry PIよりわずかに強力なARM-SoCで実行しています。このコードはCPU使用率100%、ダウンロード速度6.1 MiB/sにすぎません。カールとwgetを使用すると、CPU使用率は100%をわずかに下回り、ネットワークインターフェイス (100 MBit/sネットワークインターフェイスでは10.7 MiB/sまたは85.6 MBit/s)をほぼ完全に使用します。

ibrowseやhackneyなどの他のHTTPライブラリを使用しようとしましたが、同じ問題が引き続き発生します。私の推測では、Erlangのソケットパフォーマンスと関係がありますが、間違っている可能性があります。ですから、私の質問は、遅いダウンロード速度の正確な原因とは何か、それに対する回避策はありますか?私はhttps://github.com/puzza007/katipoのようなlibcurlを使うライブラリを知っているので、おそらく同じ問題はないでしょうが、NIFを使うライブラリを使わないほうがいいでしょう。バックbenchmarkに行く

defmodule DownloadPerformanceTest do 
    @testfile 'http://speed.hetzner.de/100MB.bin' 
    @filesize 104857600 
    @save_to '/dev/null' 

    def test() do 
    Application.start(:inets) 
    then = :erlang.system_time(:micro_seconds) 
    {:ok, :saved_to_file} = :httpc.request(:get, {@testfile, []}, [], [{:stream, @save_to}]) 
    now = :erlang.system_time(:micro_seconds) 
    diff = now - then 
    bw = bandwidth_to_human_readable(@filesize, diff) 
    IO.puts "Download took #{:erlang.trunc(diff/1_000_000)} seconds, average speed: #{bw}" 
    end 

    defp bandwidth_to_human_readable(bytes, microseconds) do 
    bytes_per_second = bytes/(microseconds/1000000) 
    exponent = :erlang.trunc(:math.log2(bytes_per_second)/:math.log2(1024)) 
    prefix = case exponent do 
       0 -> {:ok, ""} 
       1 -> {:ok, "Ki"} 
       2 -> {:ok, "Mi"} 
       3 -> {:ok, "Gi"} 
       4 -> {:ok, "Ti"} 
       5 -> {:ok, "Pi"} 
       6 -> {:ok, "Ei"} 
       7 -> {:ok, "Zi"} 
       8 -> {:ok, "Yi"} 
       _ -> {:error, :too_large} 
      end 
    case prefix do 
     {:ok, prefix} -> 
     quantity = Float.round(bytes_per_second/:math.pow(1024, exponent), 2) 
     unit = "#{prefix}B/s" 
     "#{quantity} #{unit}" 
     {:error, :too_large} -> 
     "#{bytes_per_second} B/s" 
    end 
    end 
end 
+2

ソケット操作でこれを試しましたか?私はhttpcを使っていくつかの特質があることを知りましたが、httpダウンロードでCPU使用率が高くなる原因は何か分かりません。私の経験では、Erlangのソケットパフォーマンスは、制約のあるハードウェアであっても*非常に*良いですが、他のプロトコルライブラリを使用していない純粋なソケットプロセスを書いているため、非効率的なHTTPデータがあるかどうかはわかりませんおそらくHTTPのチャンクとヘッダーなどは比較的高価な方法で逆アセンブルされますか?) – zxq9

+0

@ zxq9実際には:gen_tcpを使うと、カールした10.5 MiB/sに比べて、私は9.81 MiB/sで直接ダウンロードできます。私はまだホイールを再発明するのではなく、適切なHTTPライブラリを使う方が好きですが、ほとんどすべてのメンテナンスされたライブラリは、ハックニーやibrowseに基づいています。 – helios35

+0

Erlangの世界では、最近ではHTTPについて心配し始めましたが、実際にはほんの数箇所にすぎません。Erlangの多くの仕事は一般にウェブとつながっておらず、文化にも広がっています。そのため、ErlangのWebライブラリやXML解析や、HTML解析の狂気には本当に深く関わっていません。私はおそらく、バイナリとアトムとバイナリからストリングキャストなどの間で反転する代わりに、*文字列*としてHTTPヘッダー、チャンクなどのネイティブ解析を行うことも考えています。 – zxq9

答えて

1

、3つの明確な問題、私はあなたがベンチマークの数字を作るの外部で行われるリモートリソースを使用していること確かめる

  • ています。したがってテストのために、私はローカルリソースに変更しました
  • 第2に、ハックニーを除いて、他のライブラリのどれもがファイルにペイロードを流しません。 /dev/nullに保存しても、ファイルの保存にはコストがかかります。
  • テストを複数回実行する必要が一度

(3回でもよい)、私はdownload_loop_hackney()でアクションを保存、削除、ハックニーは最速

defp download_loop_hackney(client, file) do 
    case :hackney.stream_body(client) do 
     {:ok, _result} -> 

     #IO.binwrite(file, result) 
     download_loop_hackney(client, file) 
     :done -> 
     :ok = File.close(file) 
    end 
    end 

ベンチマークの数字は、このように

あるあります
download_http: download took 0 seconds, average speed: 211.05 MiB/s 
download_ibrowse: download took 0 seconds, average speed: 223.15 MiB/s 
download_hackney: download took 0 seconds, average speed: 295.83 MiB/s 
download_tcp: download took 0 seconds, average speed: 595.84 MiB/s 
+0

'/ dev/null'に保存した結果のオーバーヘッドについての良い点がありますが、コンテンツを保存しない' download_tcp'しかありません。あなたが得たダウンロード速度で判断すると、私は「ローカルリソース」によって、ファイルがローカルホストからダウンロードされたことを意味します。つまり、実際のネットワークインターフェイスではなくループバックデバイスが使用されました。これは、ネットワークデバイスを使用して高いCPU使用率が課せられているため、測定したいことではありません。 – helios35

関連する問題