このC++コードのパフォーマンスを向上させ、それが基になっているCコードと同等にする方法を理解しようとしています。C++ iostreamとC stdioのパフォーマンス/オーバーヘッド
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
typedef struct point {
double x, y;
} point_t;
int read_point(FILE *fp, point_t *p) {
char buf[1024];
if (fgets(buf, 1024, fp)) {
char *s = strtok(buf, " ");
if (s) p->x = atof(s); else return 0;
s = strtok(buf, " ");
if (s) p->y = atof(s); else return 0;
}
else
return 0;
return 1;
}
int main() {
point_t p;
FILE *fp = fopen("biginput.txt", "r");
int i = 0;
while (read_point(fp, &p))
i++;
printf("read %d points\n", i);
return 0;
}
C++のコードは次のようになります:
#include <iostream>
#include <fstream>
using namespace std;
struct point {
double x, y;
};
istream &operator>>(istream &in, point &p) {
return in >> p.x >> p.y;
}
int main() {
point p;
ifstream input("biginput.txt");
int i = 0;
while (input >> p)
i++;
cout << "read " << i << " points" << endl;
return 0;
}
私はC++のコードが短く、より直接的であるが、私は私のマシン上でそれらの両方を実行したときにそのようなCのコードは次のようになります(両方とも138メガバイトのテストファイルに対して同じマシン上で実行されている)非常に異なるパフォーマンスを得る:
$ time ./test-c
read 10523988 points
1.73 real 1.68 user 0.04 sys
# subsequent runs:
1.69 real 1.64 user 0.04 sys
1.72 real 1.67 user 0.04 sys
1.69 real 1.65 user 0.04 sys
$ time ./test-cpp
read 10523988 points
14.50 real 14.36 user 0.07 sys
# subsequent runs
14.79 real 14.43 user 0.12 sys
14.76 real 14.40 user 0.11 sys
14.58 real 14.36 user 0.09 sys
14.67 real 14.40 user 0.10 sys
をC++バージョンがABであるという結果は変わりません連続してプログラムのいずれかを何度も実行します10倍も遅くなります。
587.96 600.12
430.44 628.09
848.77 468.48
854.61 76.18
240.64 409.32
428.23 643.30
839.62 568.58
私が欠けているのオーバーヘッドを削減するトリックがあります:
ファイル形式は、以下のようなスペースで区切られたダブルスの行だけを、ありますか?
編集1:これは本当に問題を解決していません
14.62 real 14.47 user 0.07 sys
14.54 real 14.39 user 0.07 sys
14.58 real 14.43 user 0.07 sys
14.63 real 14.45 user 0.08 sys
14.54 real 14.32 user 0.09 sys
:オペレーターのインラインを作る非常に小さいが、おそらく検出可能な効果を持っているようです。
編集2:私は打ち鳴らすを使用しています:
$ clang --version
Apple LLVM version 7.0.0 (clang-700.0.72)
Target: x86_64-apple-darwin15.5.0
Thread model: posix
私はCまたはC++のいずれか上の任意の最適化レベルを使用していないと、彼らは両方のクラン上の同じバージョンでコンパイルされています私のMac。 OS X 10.11でXcode(/ usr/bin/clang)に付属するバージョンでしょう。私は1つではなく他のコンパイラで最適化を有効にするか、別のコンパイラを使用すると問題を解決すると考えました。
編集3:何か他
でistream &operator>>
を置き換える私はCのバージョンに近づくようにIStreamの演算子を書き換えてきたし、それが改善されているが、私はまだ〜5倍のパフォーマンスギャップを参照してください。
inline istream &operator>>(istream &in, point &p) {
string line;
getline(in, line);
if (line.empty())
return in;
size_t next = 0;
p.x = stod(line, &next);
p.y = stod(line.substr(next));
return in;
}
実行します
$ time ./test-cpp
read 10523988 points
6.85 real 6.74 user 0.05 sys
# subsequently
6.70 real 6.62 user 0.05 sys
7.16 real 6.86 user 0.12 sys
6.80 real 6.59 user 0.09 sys
6.79 real 6.59 user 0.08 sys
興味深いことに、-O3
でこれをコンパイルすることは実質的な改善である:
$ time ./test-cpp
read 10523988 points
2.44 real 2.38 user 0.04 sys
2.43 real 2.38 user 0.04 sys
2.49 real 2.41 user 0.04 sys
2.51 real 2.42 user 0.05 sys
2.47 real 2.40 user 0.05 sys
編集4:CのものとのIStreamオペレータの本体を>>交換
この場合、
inline istream &operator>>(istream &in, point &p) {
char buf[1024];
in.getline(buf, 1024);
char *s = strtok(buf, " ");
if (s)
p.x = atof(s);
else
return in;
s = strtok(NULL, " ");
if (s)
p.y = atof(s);
return in;
}
タイミング、それが最適化されていない最適化が最適化されていないC(Cはまだかかわらず、勝利最適化された)の上にそれを置く2秒の領土で私たちを取得します。上には、Cのパフォーマンスに非常に近くなります。最適化せずに、正確には:
2.13 real 2.08 user 0.04 sys
2.14 real 2.07 user 0.04 sys
2.33 real 2.15 user 0.05 sys
2.16 real 2.10 user 0.04 sys
2.18 real 2.12 user 0.04 sys
2.33 real 2.17 user 0.06 sys
で:最適化と
1.16 real 1.10 user 0.04 sys
1.19 real 1.13 user 0.04 sys
1.11 real 1.06 user 0.03 sys
1.15 real 1.09 user 0.04 sys
1.14 real 1.09 user 0.04 sys
C、ちょうどリンゴ対リンゴ行うには:私は私が一緒に暮らすことができたとし
0.81 real 0.77 user 0.03 sys
0.82 real 0.78 user 0.04 sys
0.87 real 0.80 user 0.04 sys
0.84 real 0.77 user 0.04 sys
0.83 real 0.78 user 0.04 sys
0.83 real 0.77 user 0.04 sys
これは、初心者のC++ユーザーとして、私は今疑問に思っています:
- これを別の方法でやってみる価値はありますか?私はistreamオペレータの中で何が起こるかは重要ではないと思う>>。
- これらの3つの方法以外にも優れたC++コードを構築する別の方法はありますか?
- これはイディオムですか?そうでない場合、ほとんどの人はそれが何であるかのパフォーマンスを受け入れるだけですか?
編集5:この質問はprintf関数についての答えは全く異なる、私はこれはおそらくアドレスに直接この上記3点のいずれかの重複がどのようにリンクされ、質問が表示されません。
例はlikeと似ていません。 C++バージョンはストリームオブジェクトからすべての値を読み込み、 'fscanf("%f&f "、&p-> x、&p-> y)'のようなものを使用することに匹敵します。 Cバージョンは、 'fscanf()'呼び出しのパフォーマンスを打ち破るために手作業で作られています。 C++のバージョンは、 'std :: getline()'や 'std :: istream :: getline()'を使い、文字列を解析するために同様に作られました。 – Peter
@Peter私は誰もfscanfを実際に使用しているとは思わない。それは非常に脆いです。しかし、私は 'getline'を使用するよう提案された変更を行うことを嬉しく思っています。それ以降は、文字列ストリームを作成し、iostreamの代わりに' >> 'を使用します。 –
@Peter私はここで完璧なベンチマークを作成しようとしているわけではありません。実際には、C++の初心者ユーザとして実際にはC++コードを実際に加速することに興味があります。 –