Dejwの答えを拡張(edit2):
File.open(filename,'w'){ |f|
uri = URI.parse(url)
Net::HTTP.start(uri.host,uri.port){ |http|
http.request_get(uri.path){ |res|
res.read_body{ |seg|
f << seg
#hack -- adjust to suit:
sleep 0.005
}
}
}
}
どこfilename
とはurl
文字列です。
このsleep
コマンドは、ネットワークが制限要因である場合に、CPU使用率を劇的に削減できるハックです。Net :: HTTPは、バッファ(v1.9.2では16kB)が一杯になるのを待たずにCPUが解放されるため、CPUは小さなチャンクを移動してビジー状態になります。少しの間スリープすると、バッファは書き込みと書き込みの間を埋める機会が与えられ、CPU使用率はカールソリューションに匹敵します(アプリケーションでは4〜5倍の違いがあります)。より堅牢なソリューションでは、進捗状況を調べf.pos
てタイムアウトを調整し、たとえばバッファサイズの95%をターゲットに設定できます。実際、この例では0.005の数値を取得しています。
申し訳ありませんが、Rubyにバッファがいっぱいになるまで待機させるよりエレガントな方法はわかりません。
編集:
これは、バッファを容量以下に保つように自動的に調整されるバージョンです。それは洗練されていない解決策ですが、カールするように求められているのと同じくらい高速で、CPU時間をほとんど使用しないようです。
3つの段階で機能します。意図的に長いスリープ時間を伴う短い学習期間は、完全なバッファーのサイズを確立します。ドロップ期間は、十分に満たされていないバッファが見つかるまで、より大きな係数を掛けることによって、反復ごとにスリープ時間をすばやく短縮します。次に、通常の期間中に、より小さな係数で上下に調整します。
私のルビーは少し錆びているので、これは改善できると確信しています。まず、エラー処理はありません。また、ダウンロード自体から離れてオブジェクトに分離されている可能性があるためautosleep.sleep(f.pos)
、ループで呼び出すだけですか?さらに良いことに、Net :: HTTPを変更して、バッファがいっぱいになるまで待機してから:-)
def http_to_file(filename,url,opt={})
opt = {
:init_pause => 0.1, #start by waiting this long each time
# it's deliberately long so we can see
# what a full buffer looks like
:learn_period => 0.3, #keep the initial pause for at least this many seconds
:drop => 1.5, #fast reducing factor to find roughly optimized pause time
:adjust => 1.05 #during the normal period, adjust up or down by this factor
}.merge(opt)
pause = opt[:init_pause]
learn = 1 + (opt[:learn_period]/pause).to_i
drop_period = true
delta = 0
max_delta = 0
last_pos = 0
File.open(filename,'w'){ |f|
uri = URI.parse(url)
Net::HTTP.start(uri.host,uri.port){ |http|
http.request_get(uri.path){ |res|
res.read_body{ |seg|
f << seg
delta = f.pos - last_pos
last_pos += delta
if delta > max_delta then max_delta = delta end
if learn <= 0 then
learn -= 1
elsif delta == max_delta then
if drop_period then
pause /= opt[:drop_factor]
else
pause /= opt[:adjust]
end
elsif delta < max_delta then
drop_period = false
pause *= opt[:adjust]
end
sleep(pause)
}
}
}
}
end