SlideShare a Scribd company logo
Rubyで実はwritev(2)
が使われているはなし
2015年11月7日
松下 正樹
自己紹介
● 松下 正樹
○ 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で発表
知らない間に使われている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回だけ呼ばれる
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);
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自身が読み書きのバッファを持つ
IO#writeの大まかな流れ (io.c)
IO#writeの呼び出し
↓
io_write(): レシーバがIOかどうか、IOが書き込み可能かのチェック
io_fwrite(): 文字コード変換とStringのfreeze
↓
io_binwrite(): 書き込みバッファに溜め込む
↓
io_binwrite_string(): 書き込みバッファが溢れると呼ばれる
↓
write(2) or writev(2)
io_binwrite_string()の実装: writev(2)導入前
if (書き込みバッファの中身がある) {
if (渡されたバイト列がバッファに収まる) {
if (バッファを詰めれば収まる) { 頑張って詰める; }
バイト列を書き込みバッファに収める;
}
io_fflush(fptr); /* 中でwrite(2)が呼ばれる */
}
rb_write_internal(p->fptr->fd, p->ptr, p->length);
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);
まとめ
● RubyのIOはstdioを使わず自前でシステムコールを叩く
● RubyのIOではwritev(2)が使われている
● writev(2)は、複数のバッファをアトミックに書き込む
○ write(2)同様成功するとは限らない
● writev(2)の導入によって
○ システムコール呼び出し回数を減らすことができる
○ バッファの中身と渡されたStringをアトミックに
書き出せる(かもしれない)

More Related Content

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