2
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

CとPythonでO_SYNCファイル書き込み

Last updated at Posted at 2017-04-22

CとPythonでO_SYNCファイル書き込み

概要

CとPythonでファイル書き込みの時間コストを比較する。

存在する適当なファイル('DRIVER')に対して、毎回ファイルの先頭から適当なデータ('COMMAND')を、適当な回数(10000回)だけ書き込んだ場合の1回当たりの書き込みにかかる平均時間コストを計測する。
(ファイルのopen/closeは時間コストに含めない。)

毎回ファイルの先頭からデータを書き込む理由は後述の背景のため。

背景

31313 マインドストーム EV3 で ev3dev を動作させ、モータ制御をする際にデバイスファイルに命令を送るのだが、その処理に限定してCとPythonで速度に差異があるか気になった。

準備

環境は以下の通り。

- macOS: 10.12.4
  software:
    - cc: Apple LLVM version 8.1.0 (clang-802.0.41)
    - python: 3.6.1

作成したコードは以下の通り。Pythonのファイル操作APIがいくつかあったので、3種類(io.FileIO, io.BaseIO, os)試してみる。

test_syncw.c

// cc -Weverything -Ofast test_syncw.c -o test_syncw

#include <stdlib.h>
#include <stdio.h>
#include <time.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>

#define TEST_COUNT 10000

#define DRIVER_FILE "DRIVER"
#define COMMAND "COMMAND"

int main(__attribute__((unused)) int argc, __attribute__((unused)) char* argv[]) {
    int fd = open(DRIVER_FILE, O_WRONLY | O_SYNC);
    do {
        clock_t elapsed_time;
        do {
            clock_t start_clock = clock();
            for (int i = 0; i < TEST_COUNT; ++i) {
                lseek(fd, 0, SEEK_SET);
                write(fd, COMMAND, sizeof(COMMAND));
            }
            elapsed_time = clock() - start_clock;
        } while(0);
        printf("%lfus\n", (double) elapsed_time / TEST_COUNT * (1000000.0 / CLOCKS_PER_SEC));
    } while(0);
    close(fd);
    return EXIT_SUCCESS;
}

test_syncw.py

import os
import io
import datetime
import time


TEST_COUNT = 10000
DRIVER_FILE = 'DRIVER'
COMMAND = b'COMMAND'


def test_with_fileio():
    elapsed_time = -1
    with io.FileIO(DRIVER_FILE, mode='w') as file:
        start = time.time()
        for i in range(TEST_COUNT):
            file.seek(0)
            file.write(COMMAND)
            file.flush()
        elapsed_time = (time.time() - start) * 1000000
        file.close()
    print(f'io.FileIO: {elapsed_time / TEST_COUNT}us')


def test_with_iobase():
    elapsed_time = -1
    with open(DRIVER_FILE, mode='wb', buffering=0) as file:
        start = time.time()
        for i in range(TEST_COUNT):
            file.seek(0)
            file.write(COMMAND)
            file.flush()
        elapsed_time = (time.time() - start) * 1000000
        file.close()
    print(f'io.IOBase: {elapsed_time / TEST_COUNT}us')


def test_with_os():
    elapsed_time = -1
    fd = os.open(DRIVER_FILE, flags=(os.O_WRONLY | os.O_SYNC))
    start = time.time()
    for i in range(TEST_COUNT):
        os.lseek(fd, 0, os.SEEK_SET)
        os.write(fd, COMMAND)
    os.close(fd)
    elapsed_time = (time.time() - start) * 1000000
    print(f'os: {elapsed_time / TEST_COUNT}us')


if __name__ == '__main__':
    test_with_fileio()
    test_with_iobase()
    test_with_os()

実験

コードを実行してみる。適当なファイル('DRIVER')は予め作成する必要がある。

$ touch DRIVER
$ cc -Weverything -Ofast test_syncw.c -o test_syncw
$ ./test_syncw
30.814900us
$ python test_syncw.py
io.FileIO: 3.4329us
io.IOBase: 3.3681us
os: 61.7254us

Pythonのio.FileIOとio.IOBaseはそもそもSYNCできていない。PythonのosはCよりも遅いが、これが比較するべき時間コストになる。

念のため5回繰り返してみた。

C Python (os)
1 33.2873us 58.1087us
2 33.6538us 72.6557us
3 33.2398us 64.4080us
4 34.0902us 60.7077us
5 34.0923us 62.3773us

Pythonの方がCの2倍くらい時間がかかるけど、絶対値的にはそんなに気になるレベルではないと思う。

2
2
3

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?