プロセス間通信の比較

デバイスドライバによるプロセス間通信 - ka2yanの日記デバイスドライバは、既存のプロセス間通信と比べて本当に早いのか、を測定した。

調べること

100バイト程度のメッセージ(画像データ等の大容量データを対象にしているのではない)をやりとりするプロセス間通信で一番早いのは何か?

測定するプロセス間通信は、前回の日記に書いたデバイスドライバ(共有メモリ方式)とデバイスドライバ(メッセージRead/Write)、そして、メッセージキュー(POSIX)、UNIXドメインソケット、名前付パイプ(FIFO)の5種類。
メッセージキュー(System V)は、fd として扱えないので、測定しない。

測定プログラムの概要

−2プロセス間で128バイトのメッセージを送受信を繰り返すプログラム
−2プロセスは、NON_BLOCKING でメッセージをread/write
−read/writeする前に、select()する

測定内容

−メッセージの送受信回数 1000,000回
−timeコマンドでのコマンド実行時間(厳密な送受信時間ではない)

測定環境

−CentOS5.2 (2.6.18)
−CPU:Pentium4 2.8GHz Memory:2GB

結果

プロセス間通信 real[s] user[s] sys[s]
デバイスドライバ(共有メモリ方式) 7.322 0.716 2.922
デバイスドライバ(メッセージRead/Write方式) 7.663 0.715 3.124
メッセージキュー(POSIX 6.935 0.792 2.755
UNIXドメインソケット 9.679 1.716 7.956
名前付パイプ(FIFO 8.272 1.465 6.801


real :実時間(コマンドを起動してから終了するまでの時間)
user :ユーザCPU時間(ユーザープロセスが動作している時間)
sys :システムCPU時間(カーネルでの動作時間)


なんと、メッセージキュー(POSIX)が一番早い!


デバイスドライバが一番早いことを期待していたのだが、残念。
ただ、デバイスドライバ(メッセージRead/Write方式)は、メッセージキュー(POSIX)より
速度が1割程度遅くても、プロセス間通信のログが取れるというメリットがある。


そんなに遅くはないけど、なんとかもう少し早くならないかなぁ。

TODO

デバイスドライバ(メッセージRead/Write方式)をもう少し使えるドライバにする。
# もう少し2.6ドライバーらしくする
# 複数プロセス対応
今は、2プロセスでしか使えない。
# 複数メッセージ対応
今は、1メッセージしか保持できない
# シェルスクリプトからのメッセージ受信処理の実装

測定に使ったプログラム達

実行順序は、デバイスドライバと同様で、read 側を実行しておき、write 側の引数に1000000を渡す。
プログラムが多いので、Makefile と 実行例は省略する。

メッセージキュー(POSIX

mqread.c

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/stat.h>
#include <sys/select.h>
#include <mqueue.h>
#include <fcntl.h>
#include <errno.h>

#define DPRINTF(...)
//#define DPRINTF(...) printf(__VA_ARGS__)

int main(int argc, char *argv[])
{
        mqd_t fd1, fd2;
        fd_set rfds, wfds;
        char buf[128];
        int read_start;
        unsigned int prio=10;
        struct mq_attr attr;
        char *rbuf;

        memset(&attr, 0, sizeof(attr));

        mq_unlink("/mq1");
        mq_unlink("/mq2");
        if ((fd1 = mq_open("/mq1", O_RDONLY|O_NONBLOCK|O_CREAT|O_EXCL,
                         S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH)) == -1) {
                perror("open /mq1");
                exit(-1);
        }
        if ((fd2 = mq_open("/mq2", O_WRONLY|O_NONBLOCK|O_CREAT|O_EXCL,
                         S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH)) == -1) {
                perror("open /mq2");
                exit(-1);
        }

        mq_getattr(fd1,&attr);
        if((rbuf = malloc(attr.mq_msgsize)) == NULL) {
                fprintf(stderr, "malloc error\n");
                exit(-1);
        }

        read_start=1;
        while (1) {
                if (read_start) {
                        buf[0] = '\0';

                        FD_ZERO(&rfds);
                        FD_SET(fd1, &rfds);

                        DPRINTF("selecting read...\n");
                        if (select(fd1+1, &rfds, NULL, NULL, NULL) == -1) {
                                perror("select");
                        }
                        if (FD_ISSET(fd1, &rfds)) {
                                int n;
                                n = mq_receive(fd1, rbuf, attr.mq_msgsize, &prio);
                                if (n > 0 ) {
                                        DPRINTF("%d = read [%s] prio=%d\n", n, rbuf, prio);
                                } else {
                                        perror("read");
                                        exit(-1);
                                }
                        }
                        read_start = 0;
                } else {
                        memset(buf, 'r', sizeof(buf));
                        buf[sizeof(buf)-1] = '\0';

                        FD_ZERO(&wfds);
                        FD_SET(fd2, &wfds);
                        DPRINTF("selecting write...\n");
                        if (select(fd2+1, NULL, &wfds, NULL, NULL) == -1) {
                                perror("select");
                                exit(-1);
                        }

                        if (FD_ISSET(fd2, &wfds)) {
                                if (mq_send(fd2, buf, sizeof(buf), prio) != 0) {
                                        perror("write");
                                        exit(-1);
                                }
                        }
                        read_start = 1;
//                      sleep(1);
                }
        }
        mq_close(fd1);
        mq_close(fd2);

        return 0;
}

mqwrite.c

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/stat.h>
#include <sys/select.h>
#include <mqueue.h>
#include <fcntl.h>
#include <errno.h>

#define DPRINTF(...)
//#define DPRINTF(...) printf(__VA_ARGS__)

int main(int argc, char *argv[])
{
        mqd_t fd1, fd2;
        fd_set rfds, wfds;
        char buf[128];
        int read_start;
        unsigned int prio=10;
        int nloop, i;
        struct mq_attr attr;
        char *rbuf;

        if (argc != 2) {
                printf("usage:\n./mqwrite <NLOOP>\n");
                exit(-1);
        }
        nloop = atoi(argv[1]);

        memset(&attr, 0, sizeof(attr));

        if ((fd1 = mq_open("/mq2", O_RDONLY|O_NONBLOCK)) == -1) {
                perror("open /mq2");
                exit(-1);
        }
        if ((fd2 = mq_open("/mq1", O_WRONLY|O_NONBLOCK)) == -1) {
                perror("open /mq1");
                exit(-1);
        }
        mq_getattr(fd1,&attr);
        if((rbuf = malloc(attr.mq_msgsize)) == NULL) {
                fprintf(stderr, "malloc error\n");
                exit(-1);
        }

        read_start=0;
        for(i=0; i<nloop; i++) {
                if (read_start) {
                        buf[0] = '\0';

                        FD_ZERO(&rfds);
                        FD_SET(fd1, &rfds);

                        DPRINTF("selecting read...\n");
                        if (select(fd1+1, &rfds, NULL, NULL, NULL) == -1) {
                                perror("select");
                        }

                        if (FD_ISSET(fd1, &rfds)) {
                                int n;
                                n = mq_receive(fd1, rbuf, attr.mq_msgsize, &prio);
                                if (n > 0 ) {
                                        DPRINTF("%d = read [%s] prio=%d\n", n, rbuf, prio);
                                } else {
                                        perror("read");
                                        exit(-1);
                                }
                        }
                        read_start = 0;
                } else {
                        memset(buf, 'w', sizeof(buf));
                        buf[sizeof(buf)-1] = '\0';

                        FD_ZERO(&wfds);
                        FD_SET(fd2, &wfds);
                        DPRINTF("selecting write...\n");
                        if (select(fd2+1, NULL, &wfds, NULL, NULL) == -1) {
                                perror("select");
                                exit(-1);
                        }

                        if (FD_ISSET(fd2, &wfds)) {
                                if (mq_send(fd2, buf, sizeof(buf), prio) != 0) {
                                        perror("write");
                                        exit(-1);
                                }
                        }
                        read_start = 1;
//                      sleep(1);
                }
        }
        printf("write loop= %d\n", i/2);
        mq_close(fd1);
        mq_close(fd2);

        return 0;
}
UNIXドメインソケット

sread.c

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/un.h>
#include <sys/select.h>
#include <sys/ioctl.h>
#include <errno.h>

#define DPRINTF(...)
//#define DPRINTF(...) printf(__VA_ARGS__)

#define SOCK_NAME "/tmp/test_socket"

int main(int argc, char *argv[])
{
        int    fd1, fd2;
        struct sockaddr_un    saddr;
        struct sockaddr_un    caddr;
        int    len, val;
        fd_set rfds, wfds;
        char buf[128];
        int read_start;


        if ((fd1 = socket(PF_UNIX, SOCK_STREAM, 0)) < 0) {
                perror("socket");
                exit(-1);
        }

        memset((char *)&saddr, 0, sizeof(saddr));
        saddr.sun_family = AF_UNIX;
        strcpy(saddr.sun_path, SOCK_NAME);

        unlink(SOCK_NAME);
        if (bind(fd1, (struct sockaddr *)&saddr,
                         sizeof(saddr.sun_family) + strlen(SOCK_NAME)) < 0){
                perror("bind");
                exit(-1);
        }

        if (listen(fd1, 1) < 0) {
                perror("listen");
                exit(-1);
        }

        len = sizeof(caddr);
        if ((fd2 = accept(fd1, (struct sockaddr *)&caddr, (socklen_t *)&len)) < 0) {
                perror("accept");
                exit(-1);
        }
        close(fd1);

        /* set non-blocking */
        val = 1;
        ioctl(fd2, FIONBIO, &val);

        read_start=1;
        while (1) {
                if (read_start) {
                        buf[0] = '\0';

                        FD_ZERO(&rfds);
                        FD_SET(fd2, &rfds);

                        DPRINTF("selecting read...\n");
                        if (select(fd2+1, &rfds, NULL, NULL, NULL) == -1) {
                                perror("select");
                        }

                        if (FD_ISSET(fd2, &rfds)) {
                                int n;
                                n = read(fd2, buf, sizeof(buf));
                                if (n > 0 ) {
                                        DPRINTF("%d = read [%s]\n", n, buf);
                                } else {
                                        perror("read");
                                        exit(-1);
                                }
                        }
                        read_start = 0;
                } else {
                        memset(buf, 'r', sizeof(buf));
                        buf[sizeof(buf)-1] = '\0';

                        FD_ZERO(&wfds);
                        FD_SET(fd2, &wfds);
                        DPRINTF("selecting write...\n");
                        if (select(fd2+1, NULL, &wfds, NULL, NULL) == -1) {
                                perror("select");
                                exit(-1);
                        }

                        if (FD_ISSET(fd2, &wfds)) {
                                if (write(fd2, buf, sizeof(buf)) != sizeof(buf)) {
                                        perror("write");
                                        exit(-1);
                                }
                        }
                        read_start = 1;
//                      sleep(1);
                }
        }
        close(fd2);

        return 0;
}

swrite.c

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/un.h>
#include <sys/select.h>
#include <sys/ioctl.h>
#include <errno.h>

#define DPRINTF(...)
//#define DPRINTF(...) printf(__VA_ARGS__)

#define SOCK_NAME "/tmp/test_socket"

int main(int argc, char *argv[])
{
        int    fd;
        struct sockaddr_un    addr;
        int    len, val;
        fd_set rfds, wfds;
        char   buf[128];
        int    i, nloop;
        int    read_start;

        if (argc != 2) {
                printf("usage:\n./swrite <NLOOP>\n");
                exit(-1);
        }
        nloop = atoi(argv[1]);

        if ((fd = socket(PF_UNIX, SOCK_STREAM, 0)) < 0) {
                perror("socket");
                exit(-1);
        }

        memset((char *)&addr, 0, sizeof(addr));
        addr.sun_family = AF_UNIX;
        strcpy(addr.sun_path, SOCK_NAME);

        /* set non-blocking */
        val = 1;
        ioctl(fd, FIONBIO, &val);

        if (connect(fd, (struct sockaddr *)&addr,
                                sizeof(addr.sun_family) + strlen(SOCK_NAME)) < 0){
                perror("connect");
                exit(1);
        }

        read_start=0;
        for (i=0; i<nloop; i++) {
                if (read_start) {
                        buf[0] = '\0';

                        FD_ZERO(&rfds);
                        FD_SET(fd, &rfds);

                        DPRINTF("selecting read...\n");
                        if (select(fd+1, &rfds, NULL, NULL, NULL) == -1) {
                                perror("select");
                        }

                        if (FD_ISSET(fd, &rfds)) {
                                int n;
                                n = read(fd, buf, sizeof(buf));
                                if (n > 0 ) {
                                        DPRINTF("%d = read [%s]\n", n, buf);
                                } else {
                                        perror("read");
                                        exit(-1);
                                }
                        }
                        read_start = 0;
                } else {
                        memset(buf, 'w', sizeof(buf));
                        buf[sizeof(buf)-1] = '\0';

                        FD_ZERO(&wfds);
                        FD_SET(fd, &wfds);
                        DPRINTF("selecting write...\n");
                        if (select(fd+1, NULL, &wfds, NULL, NULL) == -1) {
                                perror("select");
                                exit(-1);
                        }

                        if (FD_ISSET(fd, &wfds)) {
                                if (write(fd, buf, sizeof(buf)) != sizeof(buf)) {
                                        perror("write");
                                        exit(-1);
                                }
                        }
                        read_start = 1;
//                      sleep(1);
                }
        }
        printf("write loop= %d\n", i/2);
        close(fd);

        return 0;
}
名前付きパイプ(FIFO

fread.c

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/stat.h>
#include <sys/select.h>
#include <fcntl.h>
#include <errno.h>

#define DPRINTF(...)
//#define DPRINTF(...) printf(__VA_ARGS__)

int main(int argc, char *argv[])
{
        int fd1, fd2;
        fd_set rfds, wfds;
        char buf[128];
        int read_start;

        unlink("/tmp/fifo1");
        unlink("/tmp/fifo2");

        if (mkfifo("/tmp/fifo1", 0666) == -1) {
                perror("mkfifo /tmp/fifo1");
                exit(-1);
        }
        if (mkfifo("/tmp/fifo2", 0777) == -1) {
                perror("mkfifo /tmp/fifo2");
                exit(-1);
        }

        if ((fd1 = open("/tmp/fifo1", O_RDONLY|O_NONBLOCK)) == -1) {
                perror("open /tmp/fifo1");
                exit(-1);
        }
        if ((fd2 = open("/tmp/fifo2", O_RDWR|O_NONBLOCK)) == -1) {
                perror("open /tmp/fifo2");
                exit(-1);
        }

        read_start=1;
        while (1) {
                if (read_start) {
                        buf[0] = '\0';

                        FD_ZERO(&rfds);
                        FD_SET(fd1, &rfds);

                        DPRINTF("selecting read...\n");
                        if (select(fd1+1, &rfds, NULL, NULL, NULL) == -1) {
                                perror("select");
                        }

                        if (FD_ISSET(fd1, &rfds)) {
                                int n;
                                n = read(fd1, buf, sizeof(buf));
                                if (n > 0 ) {
                                        DPRINTF("%d = read [%s]\n", n, buf);
                                } else {
                                        perror("read");
                                        exit(-1);
                                }
                        }
                        read_start = 0;
                } else {
                        memset(buf, 'r', sizeof(buf));
                        buf[sizeof(buf)-1] = '\0';

                        FD_ZERO(&wfds);
                        FD_SET(fd2, &wfds);
                        DPRINTF("selecting write...\n");
                        if (select(fd2+1, NULL, &wfds, NULL, NULL) == -1) {
                                perror("select");
                                exit(-1);
                        }

                        if (FD_ISSET(fd2, &wfds)) {
                                if (write(fd2, buf, sizeof(buf)) != sizeof(buf)) {
                                        perror("write");
                                        exit(-1);
                                }
                        }
                        read_start = 1;
//                      sleep(1);
                }
        }
        close(fd1);
        close(fd2);

        return 0;
}
                                                     

fwrite.c

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/stat.h>
#include <sys/select.h>
#include <fcntl.h>
#include <errno.h>

#define DPRINTF(...)
//#define DPRINTF(...) printf(__VA_ARGS__)

int main(int argc, char *argv[])
{
        int fd1, fd2;
        fd_set rfds, wfds;
        char buf[128];
        int read_start;
        int nloop, i;

        if (argc != 2) {
                printf("usage:\n./fwrite <NLOOP>\n");
                exit(-1);
        }
        nloop = atoi(argv[1]);

        if ((fd1 = open("/tmp/fifo2", O_RDONLY|O_NONBLOCK)) == -1) {
                perror("open /tmp/fifo2");
                exit(-1);
        }
        if ((fd2 = open("/tmp/fifo1", O_WRONLY|O_NONBLOCK)) == -1) {
                perror("open /tmp/fifo1");
                exit(-1);
        }

        read_start=0;
        for (i=0; i<nloop; i++) {
                if (read_start) {
                        buf[0] = '\0';

                        FD_ZERO(&rfds);
                        FD_SET(fd1, &rfds);

                        DPRINTF("selecting read...\n");
                        if (select(fd1+1, &rfds, NULL, NULL, NULL) == -1) {
                                perror("select");
                        }

                        if (FD_ISSET(fd1, &rfds)) {
                                int n;
                                n = read(fd1, buf, sizeof(buf));
                                if (n > 0 ) {
                                        DPRINTF("%d = read [%s]\n", n, buf);
                                } else {
                                        perror("read");
                                        exit(-1);
                                }
                        }
                        read_start = 0;
                } else {
                        memset(buf, 'w', sizeof(buf));
                        buf[sizeof(buf)-1] = '\0';

                        FD_ZERO(&wfds);
                        FD_SET(fd2, &wfds);
                        DPRINTF("selecting write...\n");
                        if (select(fd2+1, NULL, &wfds, NULL, NULL) == -1) {
                                perror("select");
                                exit(-1);
                        }

                        if (FD_ISSET(fd2, &wfds)) {
                                if (write(fd2, buf, sizeof(buf)) != sizeof(buf)) {
                                        perror("write");
                                        exit(-1);
                                }
                        }
                        read_start = 1;
//                      sleep(1);
                }
        }
        printf("write loop= %d\n", i/2);
        close(fd1);
        close(fd2);

        return 0;
}