2017-10-16 138 views
1

使用Erlang的httpc通過HTTP下載文件時,CPU利用率遠高於curl或wget。我用來測量下載速度的代碼可以在這篇文章的底部看到。下載文件時的CPU利用率高

高CPU利用率問題尤其是在低端設備上。我在一個ARM-SoC上運行Erlang,它比第一個Raspberry PI稍微強大一些,而且這段代碼的CPU利用率高達100%,下載速度僅爲6.1 MiB/s。使用curl和wget,CPU利用率保持略低於100%,並且幾乎完全利用網絡接口 (100 MBit/s網絡接口上的10.7 MiB/s或85.6 MBit/s)。

我嘗試過使用其他HTTP庫,包括ibrowse和hackney,但同樣的問題仍然存在。我的猜測是它與Erlang的套接字性能有關,但我可能是錯的。所以我的問題是,究竟是什麼造成了這些緩慢的下載速度,並且是否有任何解決方法?我知道使用libcurl的庫如https://github.com/puzza007/katipo,因此可能不會有相同的問題,但我不希望使用任何使用NIF的庫。

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直接允許我以9.81 MiB/s的速度進行下載,而使用curl的速度爲10.5 MiB/s。我仍然更喜歡使用適當的HTTP庫,而不是重新發明輪子,但似乎幾乎所有維護良好的庫都基於hackney或ibrowse。 – helios35

+0

在Erlang世界中,我們最近才真正開始關注HTTP,實際上,這只是其中的幾個地方 - 很多Erlang的工作通常與Web無關,並且也延伸到了文化領域。出於這個原因,我們從來沒有真正深入到本地的Erlang網絡庫或XML解析或瘋狂的HTML解析。我想這可能也適用於本地解析HTTP標頭,分塊等,作爲* strings *而不是在二進制文件和原子之間翻轉以及二進制到字符串轉換等。 – zxq9

回答

1

回過頭來看看這個benchmark,三清的問題,我能夠探明

  • 您正在使用的是由外部因素使基準數實現遠程資源。因此,爲了測試,我更改爲本地資源
  • 其次,除hackney之外,沒有其他庫將有效負載傳輸到文件。雖然保存到/dev/null,但文件保存有成本。
  • 測試需要運行一次以上(可能是三倍)

有一次,我刪除了攢動的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