Successfully reported this slideshow.
We use your LinkedIn profile and activity data to personalize ads and to show you more relevant ads. You can change your ad preferences anytime.

Rubyで実はwritev(2) が使われているはなし

7,109 views

Published on

大江戸Ruby会議05で発表した内容です

Published in: Engineering
  • Be the first to comment

Rubyで実はwritev(2) が使われているはなし

  1. 1. Rubyで実はwritev(2) が使われているはなし 2015年11月7日 松下 正樹
  2. 2. 自己紹介 ● 松下 正樹 ○ svn: glass, twitter: @_mmasaki, github: mmasaki ● NTTコミュニケーションズ ● 136 commits for Ruby ○ 高速化: String#include?, Hash#flatten. Marshal.load ● 16 commits for OpenStack (Liberty) ○ “I like Python too.”と書いたらCFP通ったので OpenStack Summit Tokyoで発表
  3. 3. 知らない間に使われているwritev(2) # 書き込みバッファのサイズは8192バイト str = "a" * 5000 File.open("foo", "w") do |f| f.write(str) # 書き込みバッファに収まる f.write(str) # 収まらない! end ● 下記のコードではwrite(2)が2回呼ばれ…ない ● writev(2)が1回だけ呼ばれる
  4. 4. writev(2)とは? ● 複数のバッファの内容をアトミックに 書き込めるかもしれないシステムコール ○ write(2)同様バッファを全て書き込めるとは限らない struct iovec { void *iov_base; size_t iov_len; }; struct iovec vector[2]; /* 2つのバッファを書き出す例 */ vector[0].iov_base = buf1; vector[0].iov_len = strlen(buf1); vector[1].iov_base = buf2; vector[1].iov_len = strlen(buf2); writev(1, vector, 2);
  5. 5. RubyのIO typedef struct rb_io_t { FILE *stdio_file; int fd; /* file descriptor */ int mode; /* mode */ (中略) rb_io_buffer_t wbuf, rbuf; (後略) } struct rb_io_buffer_t { char *ptr; int off; int len; int capa; } ● stdioを使わず直接システムコールを使っている ● ruby自身が読み書きのバッファを持つ
  6. 6. IO#writeの大まかな流れ (io.c) IO#writeの呼び出し ↓ io_write(): レシーバがIOかどうか、IOが書き込み可能かのチェック io_fwrite(): 文字コード変換とStringのfreeze ↓ io_binwrite(): 書き込みバッファに溜め込む ↓ io_binwrite_string(): 書き込みバッファが溢れると呼ばれる ↓ write(2) or writev(2)
  7. 7. io_binwrite_string()の実装: writev(2)導入前 if (書き込みバッファの中身がある) { if (渡されたバイト列がバッファに収まる) { if (バッファを詰めれば収まる) { 頑張って詰める; } バイト列を書き込みバッファに収める; } io_fflush(fptr); /* 中でwrite(2)が呼ばれる */ } rb_write_internal(p->fptr->fd, p->ptr, p->length);
  8. 8. io_binwrite_string()の実装: writev(2)導入後 struct iovec iov[2]; /* iov[0]: 書き込みバッファ */ iov[0].iov_base = fptr->wbuf.ptr+fptr->wbuf.off; iov[0].iov_len = fptr->wbuf.len; /* iov[1]: 書き込みバッファに収まらなかったバイト列 */ iov[1].iov_base = (char *)p->ptr; iov[1].iov_len = p->length; r = rb_writev_internal(fptr->fd, iov, 2);
  9. 9. まとめ ● RubyのIOはstdioを使わず自前でシステムコールを叩く ● RubyのIOではwritev(2)が使われている ● writev(2)は、複数のバッファをアトミックに書き込む ○ write(2)同様成功するとは限らない ● writev(2)の導入によって ○ システムコール呼び出し回数を減らすことができる ○ バッファの中身と渡されたStringをアトミックに 書き出せる(かもしれない)

×