質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.50%
C

C言語は、1972年にAT&Tベル研究所の、デニス・リッチーが主体となって作成したプログラミング言語です。 B言語の後継言語として開発されたことからC言語と命名。そのため、表記法などはB言語やALGOLに近いとされています。 Cの拡張版であるC++言語とともに、現在世界中でもっとも普及されているプログラミング言語です。

Q&A

解決済

3回答

9026閲覧

システムコールが遅い言われる所以について

mshg

総合スコア7

C

C言語は、1972年にAT&Tベル研究所の、デニス・リッチーが主体となって作成したプログラミング言語です。 B言語の後継言語として開発されたことからC言語と命名。そのため、表記法などはB言語やALGOLに近いとされています。 Cの拡張版であるC++言語とともに、現在世界中でもっとも普及されているプログラミング言語です。

0グッド

1クリップ

投稿2016/12/09 17:58

###前提
C言語でシステムコールが遅いと言われる所以について調べました。
write,readなどのシステムコールではバッファリングを行わないためカーネルのコンテキストスイッチが増えオーバーヘッドになっているということがわかりました(一部の要因なのかもしれません)。そこで自前でバッファを確保してそこに入れる様にしてやればオーバーヘッドを減らせるのでは?と思い実験プログラムを書いてみたのですが一部が予想と反した結果になりました。

環境
macOS Sierra
バージョン 10.12.1
xcodeインストール済み

システムコール回数を調べるために使ったコマンド
dtruss -c ./a.out

###該当のソースコード
▼512バイトのバッファ有り

c

1#include <stdio.h> 2#include <stdlib.h> 3#include <unistd.h> 4#include <sys/types.h> 5#include <sys/uio.h> 6#include <fcntl.h> 7 8int main(int argc, char *argv[]){ 9 10 ssize_t n; 11 char buf[512]; 12 int fd; 13 int df; 14 15 if(argc != 3){ 16 fprintf(stderr,"usage:%s [srcfile] [dstfile]\n", argv[0]); 17 exit(EXIT_FAILURE); 18 } 19 20 //読み 21 if((fd = open(argv[1], O_RDONLY)) < 0) perror(argv[1]); 22 23 //書き 24 if((df = open(argv[2], O_WRONLY | O_CREAT | O_TRUNC, 0666)) < 0) perror(argv[2]); 25 26 size_t count; //ループの回数 27 28 /*データがなくなるまでループ*/ 29 while((n = read(fd, buf, sizeof(buf))) > 0){ 30 write(df, buf, n); 31 count++; 32 } 33 34 printf("ループカウンタ:%zu\n", count); 35 36 close(fd); 37 close(df); 38 return 0; 39}

▼バッファなし

c

1#include <stdio.h> 2#include <stdlib.h> 3#include <unistd.h> 4#include <sys/types.h> 5#include <sys/uio.h> 6#include <fcntl.h> 7 8int main(int argc, char *argv[]){ 9 10 ssize_t n; 11 char c; 12 int fd; 13 int df; 14 15 if(argc != 3){ 16 fprintf(stderr,"usage:%s [srcfile] [dstfile]\n", argv[0]); 17 exit(EXIT_FAILURE); 18 } 19 20 //読み 21 if((fd = open(argv[1], O_RDONLY)) < 0) perror(argv[1]); 22 23 //書き 24 if((df = open(argv[2], O_WRONLY | O_CREAT | O_TRUNC, 0666)) < 0) perror(argv[2]); 25 26 size_t count; //ループの回数 27 28 /*データがなくなるまでループ*/ 29 while((n = read(fd, &c, sizeof(c))) > 0){ 30 write(df, &c, n); 31 count++; 32 } 33 34 printf("ループカウンタ:%zu\n", count); 35 36 close(fd); 37 close(df); 38 return 0; 39}

###実験プログラムの結果
512kバイトの元データをプログラムに与えた場合

------512バイトのバッファ有り-------
・標準出力結果
ループカウンタ:1000

・システムコール回数
write 1000
read 1001

・実行速度
real 0m0.036s
user 0m0.003s
sys 0m0.018s

・コピー先ファイルのサイズ
512kバイト コピーできている

-----バッファなし(1バイトずつ処理)-----
・標準出力結果
ループカウンタ:512000

・システムコール回数
write 8075
read 8076

・実行速度
real 0m0.653s
user 0m0.094s
sys 0m0.552s

・コピー先のファイルサイズ
512kバイト コピーできている

###疑問点
512kバイトのデータを512バイトのバッファに格納して処理する場合、システムコールを1000回呼んで1001回目にデータがもうないのでループを抜けるのはわかります。結果もそのようになったので納得です。

一方、バッファなしで1バイトずつ処理する場合は、readを512000回呼んで512001回目にデータがもうないのでループを抜けると思うし、ループカウンタも512000回になっているのですが、システムコールは8075回という値が示されてしまいます。自分はシステムコール回数の結果も512000回だと思うのですがなぜこの様な結果になってしまうのでしょうか?
コピー先のファイルサイズはちゃんと512kバイトあるのでプログラムとしてはちゃんと動いていそうなのですが...
よろしくお願いします。

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

guest

回答3

0

ベストアンサー

dtruss (というかそれが呼び出しているdtrace) は、プローブ結果をメモリ上のバッファに保持し、一定のタイミングでまとめて出力します。結果が多すぎてバッファがあふれると、単にバッファの内容を捨てます。次のようなメッセージが出力されていれば、バッファがあふれたために出力できなかった情報があります。

dtrace: 12345 drops on CPU 0

主バッファのサイズは、dtruss -b サイズとすれば変えられます。サイズの初期値は4mです。

プローブ結果が多いと、バッファサイズを増やしただけでは追いつかないかもしれません (ご質問の例については、私の環境では1gまで増やしても記録落ちしました)。

dtrussの実体はシェルスクリプトで、内部でdtraceを起動して結果を出力しています。その際にdtraceの設定もしているので、これを書き換えればもっと細かいチューニングができます (おそらく、スクリプト中に「#pragma D ...」などと記述してある箇所がそうではないかと思います)。dtraceのチューニングパラメータについては次のような資料を参考にして下さい。

Solaris 動的トレースガイド:

投稿2016/12/12 03:01

ikedas

総合スコア4227

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

mshg

2016/12/13 02:16

記録落ちしていたのですね!詳しい回答ありがとうございます!
guest

0

回答では無いですが、Linuxで、strace -cで調べると下記になりました。

ループカウンタ:512000 % time seconds usecs/call calls errors syscall ------ ----------- ----------- --------- --------- ---------------- 55.36 2.558412 5 512001 write 44.64 2.062902 4 512002 read 0.00 0.000000 0 8 4 open 0.00 0.000000 0 4 close 0.00 0.000000 0 4 3 stat 0.00 0.000000 0 3 fstat 0.00 0.000000 0 9 mmap 0.00 0.000000 0 3 mprotect 0.00 0.000000 0 1 munmap 0.00 0.000000 0 1 brk 0.00 0.000000 0 1 1 access 0.00 0.000000 0 1 execve 0.00 0.000000 0 1 arch_prctl ------ ----------- ----------- --------- --------- ---------------- 100.00 4.621314 1024039 8 total

投稿2016/12/10 10:00

otn

総合スコア84423

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

KSwordOfHaste

2016/12/10 13:50

なるほどLinuxだとwrite()の呼び出しはそのままsystem callになるのですね・・・
mshg

2016/12/11 02:56

なるほどlinuxだとなるんですね...有難うございます
guest

0

Macをもっておらずなぜかを知ってもいないのですが・・・

Cのwriteが実際にはシステムコールに展開されないという可能性はないのでしょうか。オブジェクトファイルを逆アセンブルするか、もしくはCコンパイラーの-Sオプション(?)でアセンブリーソースが出力できるのであればそれを観察してみるとwriteの呼び出しが直接システムコールとして展開されるか観察できる気がします。

投稿2016/12/09 18:58

編集2016/12/11 03:47
KSwordOfHaste

総合スコア18392

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

mshg

2016/12/09 22:28

回答有難うございます。 なるほどアセンブラソースの観察は怠っていました、観察をしてみたいと思います。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.50%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問