pythonの file.write と os.write の違い

python で file.writeで書き込むのと、os.writeで書き込むのってどう違うの?

私は、ファイルを開いて書き込むとき、ライブラリで file objectを渡す時があるから f = open() で呼び出して、それをos.writeに渡すのは f.fileno()しなきゃいけないから面倒だし、何気なく f.write()で書いてました。

ふと、sync writeで書き込む機会があって、あれ?なんかすごい遅いぞ?と気づいたのです。同じ条件でddなら速い。色々試して、os.write()で書いてみたら速いことがわかりました。

1MBのファイルデータを書き込んだら、50倍遅い。。こりゃひどい。asyncで書いても2倍遅い。

なんで?

pythonのソース(2.6 です。3.xは知らないです。)を読んでみると、file_writeは書き込みの時に fwrite(3)をサイズ1で呼び出してる! 1MBのファイル書くのに百万回 I/O叩いてたわけか、こりゃ、遅いわけだ。。

static PyObject *
file_write(PyFileObject *f, PyObject *args)
{
        char *s;
        Py_ssize_t n, n2;
        if (f->f_fp == NULL)
                return err_closed();
        if (!PyArg_ParseTuple(args, f->f_binary ? "s#" : "t#", &s, &n))
                return NULL;
        f->f_softspace = 0;
        Py_BEGIN_ALLOW_THREADS
        errno = 0;
        n2 = fwrite(s, 1, n, f->f_fp);
        Py_END_ALLOW_THREADS
        if (n2 != n) {
                PyErr_SetFromErrno(PyExc_IOError);
                clearerr(f->f_fp);
                return NULL;
        }
        Py_INCREF(Py_None);
        return Py_None;
}

かたや、os.write (posix_write)は普通にwrite(2)を読んでるっぽい。

static PyObject *
posix_write(PyObject *self, PyObject *args)
{
        int fd;
        Py_ssize_t size;
        char *buffer;

        if (!PyArg_ParseTuple(args, "is#:write", &fd, &buffer, &size))
                return NULL;
        Py_BEGIN_ALLOW_THREADS
        size = write(fd, buffer, (size_t)size);
        Py_END_ALLOW_THREADS
        if (size < 0)
                return posix_error();
        return PyInt_FromSsize_t(size);
}

と、いうことで、f.write()を使うべき理由がよくわからん。とりあえず os.write()つかっとこっと。