From: "Glass_saga (Masaki Matsushita)" Date: 2012-04-02T22:17:14+09:00 Subject: [ruby-dev:45488] [ruby-trunk - Feature #6082] io_binwrite()内でwritev()を使う Issue #6082 has been updated by Glass_saga (Masaki Matsushita). File shift_fflush.diff added File binwrite.diff added 次のようなベンチマークを実行してみましたが、結果にばらつきはあるものの意味のある高速化にはならなかったようです。 require 'benchmark' require 'tempfile' str = "a" * 8000 Tempfile.open("foo") do |f| Benchmark.bm do |x| x.report do 20000.times { f.write str } end end end trunk(r35215): user system total real 0.070000 0.240000 0.310000 ( 0.346534) user system total real 0.110000 0.270000 0.380000 ( 0.418857) user system total real 0.070000 0.270000 0.340000 ( 0.512318) proposal: user system total real 0.050000 0.240000 0.290000 ( 0.363697) user system total real 0.050000 0.220000 0.270000 ( 0.428360) user system total real 0.080000 0.260000 0.340000 ( 0.518545) 但し、modeにFile::SYNCを指定している場合には格段に速くなるようです。 require 'benchmark' require 'tempfile' str = "a" * 8000 Tempfile.open("foo", nil, nil,{:mode => File::SYNC}) do |f| Benchmark.bm do |x| x.report do 1000.times { f.write str } end end end trunk(r35215): user system total real 0.020000 0.280000 0.300000 ( 1.452105) user system total real 0.010000 0.270000 0.280000 ( 1.452677) user system total real 0.010000 0.220000 0.230000 ( 1.348059) proposal: user system total real 0.010000 0.120000 0.130000 ( 0.714809) user system total real 0.020000 0.120000 0.140000 ( 0.669088) user system total real 0.010000 0.210000 0.220000 ( 0.833870) しかし、同期モードでの書き込みが速くなる事にどれほどの意義があるのかはわかりません。 straceで見るとproposalでは確かにwritev()が使われているので、trunkにおいてmemmove()やシステムコールの呼び出しにかかるコストと、 proposalにおいてiovec構造体の配列を造るのにかかるコストが同程度な為に、同期モードでない場合には高速化に至らなかったものと思われます。 始めからきちんとベンチマークを取っていれば良かったのですが、お騒がせしました。 #ifdefを小さくする為に書いた2つのpatchをご参考までに添付します(速度面ではどちらも変わりません)。 「ここがおかしいから直せば速くなる」などありましたら指摘して下さると幸いです。 ---------------------------------------- Feature #6082: io_binwrite()内でwritev()を使う https://bugs.ruby-lang.org/issues/6082#change-25599 Author: Glass_saga (Masaki Matsushita) Status: Feedback Priority: Normal Assignee: Glass_saga (Masaki Matsushita) Category: core Target version: io.cのio_binwrite()内で、writev()を用いる事を提案します。 現在のio_binwrite()は、実際にシステムコールを呼ぶ時には渡された文字列がwbufに収まるならばMEMMOVE()で収めてしまってio_fflush()し、 収まらなければfflushした後に再度rb_write_internal()を呼んでいますが、writev()を用いる事でMEMMOVE()の必要がなくなり、 wbufと渡された文字列を結合する事なく一度のシステムコールで書き出す事ができます。 また、wbufと渡された文字列を1度に書き出す事に成功すれば、他のスレッドからのwriteが間に入らないというメリットもあります。 patchを添付します。 test-allを実行したところ、test_keep_alive_EOF(TestNetHTTPKeepAlive)でfailure、test_inspect_nonascii(TestDir_M17N)でerrorとなりますが、 これはtrunkのr34799でも同様なので、このpatchによる問題ではないものと思われます。 -- http://bugs.ruby-lang.org/