UNIXネットワークプログラミングのお勉強 (3) PF_UNIX vs. PF_INET

そういえば一か月前はネットワークプログラミングを勉強していたことを思い出した.案の定,すべてを忘れていたのでもう一度復習.


マシン内の通信の場合,TCP通信 (PF_INET) 以外にファイルを介したソケット通信 (PF_UNIX) という選択肢がある.どれくらいパフォーマンスが違うのだろうと思って,PF_UNIXとPF_INETでかかる時間を計測してみた.

実験条件

    • server, clientの1対1通信
    • connectし終わってから100万send/recvにかかる時間を計測
    • 一回の通信で送信されるデータサイズを変更して比較 (64,1024,8192,16384,32768bytes)
    • 時間計測はgettimeofdayを使用
    • 各10回試行して平均を算出

結果

表のsizeはbyte.各方式の値は平均時間 (sec.).


  • 基本的にPF_INETの方が速い.ディスクI/Oで時間かかっていると思われる.
  • straceで確認したところ,システムコール的な違いはなさそう.
  • データサイズが8192bytesの場合,PF_INETにおいて急に時間がかかるようになった!
    • おかしいと思って,何回もやりなおしたところ,平均はあまり変化ないものの,分散が非常に大きいという結果になった.
    • パケットサイズが関係しているのかもしれないが,詳しい原因はわからないので放置.

まとめ

ネットワークプログラミングはTCP (PF_INET) でいいんじゃない?


Appendix

PF_UNIXに用いたソースコード.PF_INETもソケット作成部分以外,基本的に同じ

server.c
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/un.h>
#include <sys/types.h>
#include <sys/socket.h>


#define SERVER "sock_s"
#define BUFFLEN 40960

int
main (int argc, char *argv[])
{

  int soc;

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

  struct sockaddr_un sun;
  sun.sun_family = AF_UNIX;
  strcpy(sun.sun_path, SERVER);
  int len = sizeof(sun.sun_family) + strlen(sun.sun_path);

  if (bind(soc, (struct sockaddr *)&sun, len) < 0) {
    perror("bind");
    exit(1);
  }

  if (listen(soc, 5) < 0) {
    perror("listen");
    exit(1);
  }


  int fd;
  int sunlen = sizeof(sun);

  char buff[BUFFLEN];
  for (;;) {
    if ((fd = accept(soc, (struct sockaddr *)&sun, (socklen_t *)&sunlen)) < 0) {
      perror("accept");
      exit(1);
    }

    while (recv(fd, buff, BUFFLEN, 0) > 0) {
      // printf("Received message: %s\n", buff);
    }

    close(fd);
  }

  close(soc);
  unlink(SERVER);

  return 0;
}
client.c
#include <stdio.h>
#include <stdlib.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <sys/un.h>
#include <unistd.h>
#include <string.h>
#include <sys/time.h>

#define SERVER "sock_s"
#define BUFFLEN 40960
#define DATASIZE 64


double
gettimeofday_sec (void)
{
  struct timeval tv;
  gettimeofday(&tv, NULL);
  return tv.tv_sec + (double)tv.tv_usec * 1e-6;
}


int
main (void)
{

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

  struct sockaddr_un sun;
  sun.sun_family = AF_UNIX;

  strcpy(sun.sun_path, SERVER);
  int len = sizeof(sun.sun_family) + strlen(sun.sun_path);

  if (connect(soc, (struct sockaddr *)&sun, len) < 0) {
    perror("connect");
    exit(1);
  }

  char buff[BUFFLEN];
  // strcpy(buff, "HELLO");
  memset(buff, 'a', DATASIZE);
  buff[ DATASIZE ] = '\0';

  int i;
  int loop = 1000000;
  double begin = gettimeofday_sec();
  for (i = 0; i < loop; i++) {
    if (send(soc, buff, strlen(buff) + 1, 0) < 0) {
      perror("send");
      exit(1);
    }
  }
  double end = gettimeofday_sec();
  //  printf("time=%lf sec\n", end - begin);
  printf("%lf\n", end - begin);


  shutdown(soc, SHUT_RDWR);
  close(soc);

  return 0;
}