私は現在、C++11 とその優れた機能を学ぼうとしています。具体的には、効率の高い汎用性を探しています。そこで、C++11 で入力ファイルの行をソートするプログラムを喜んで作成し、私の新鮮なスキルをテストしました。C++ コンパイラのインライン化と優れた機能により、この小さな例では高いパフォーマンスが期待できました。私のプログラムがどれほど高速であったかについてのヒントを得るために、関数を使用して C でまったく同じプログラムをハッキングしましたqsort
。これは生の C であり、この関数に対してインライン化は実行されず、比較関数は間接指定で呼び出され、2 つの間接指定を実行する必要があるためです。char *
文字列を表すポインタにアクセスします。
事実
それでも、結果には非常に驚きました。C は C++ よりも 4 倍速いようです。8Mb ファイルでは、次の結果が得られます。
$ g++ -O3 -std=c++11 -o sort sort.C
$ time ./sort < huge > /dev/null
real 0m0.415s
user 0m0.397s
sys 0m0.013s
$ cc -O3 -Wall -o sortc sort.c
$ time ./sortc < huge > /dev/null
real 0m0.104s
user 0m0.097s
sys 0m0.010s
$ wc -l huge
140190 huge
可能な限り公平にしようとしたことに注意してください。コンパイル オプションは同じであり、私の C プログラム (後でダンプされます) は C++ と同じように動作します。入力行のサイズに制限がなく、入力数に制限もありません。行。
malloc
また、私の C プログラムは入力行ごとにほぼ 1 回呼び出すのに対し、C++ プログラムでは入力行ごとに 10 回の割り当てがあることにも気付きました。
コード
比較に使用した 2 つのプログラムを次に示します。
#include <iostream>
#include <string>
#include <vector>
#include <algorithm>
#include <memory>
int main () {
typedef std::vector<std::string> svec;
svec a;
std::string s;
for (;;) {
getline(std::cin, s);
if (std::cin.eof()) {
if (s != "")
a.push_back(std::move(s));
break;
}
a.push_back(std::move(s));
}
std::sort(a.begin(), a.end());
for (std::string &s : a) {
std::cout << s << "\n";
}
}
そして、私のはるかに冗長なCバージョン。
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#define BUFSZ 100
size_t getl(char **line, size_t len) {
char buf[BUFSZ];
size_t i, n;
for (i=0; i<BUFSZ; i++) {
int c = getchar();
if (c == EOF || c == '\n') {
*line = malloc(len+i+1);
memcpy(&(*line)[len], buf, i);
(*line)[len+i] = 0;
return i;
}
buf[i] = c;
}
n = getl(line, len+i);
memcpy(&(*line)[len], buf, i);
return i+n;
}
#define ARRAYSZ 30
struct Array {
char **lv;
size_t li, lc;
};
void addline(struct Array *a, char *line) {
if (a->li == a->lc) {
a->lc *= 2;
a->lv = realloc(a->lv, a->lc * sizeof *a->lv);
}
a->lv[a->li++] = line;
}
int cmp(const void *a, const void *b) {
return strcmp(*(const char **)a, *(const char **)b);
}
int main(void) {
char *line;
struct Array a;
size_t i;
a.li = 0;
a.lc = ARRAYSZ;
a.lv = malloc(a.lc * sizeof *a.lv);
for (;;) {
getl(&line, 0);
if (feof(stdin)) {
if (line[0] != 0)
addline(&a, line);
else
free(line);
break;
}
addline(&a, line);
}
qsort(a.lv, a.li, sizeof *a.lv, cmp);
for (i=0; i<a.li; i++) {
printf("%s\n", a.lv[i]);
free(a.lv[i]);
}
free(a.lv);
return 0;
}
質問
私の C++ プログラムのどこを (単純な C にならずに) 高速化するために変更する必要があるか教えてもらえますか? 私は非常にイディオムにとどまろうとしました.C++でハックするのは良い方法ですか、それとも高性能が必要な場合はCのようなコードを書く傾向がありますか? C++ プログラムがヒープにそれほど多くを割り当てているのはなぜですか? どうすればこれを減らすことができますか?
編集
多くの要望により、C++ プログラムのプロファイリングの結果を表示します。これは、私の C++ プログラムのプロファイラーの面白い出力です (最初の 2 行)。
Each sample counts as 0.01 seconds.
% cumulative self self total
time seconds seconds calls ms/call ms/call name
40.03 0.02 0.02 1198484 0.00 0.00 __gnu_cxx::__normal_iterator<std::string*, std::vector<std::string, std::allocator<std::string> > >::operator--()
30.02 0.04 0.02 2206802 0.00 0.00 bool std::operator< <char, std::char_traits<char>, std::allocator<char> >(std::basic_string<char, std::char_traits<char>, std::allocator<char> > const&, std::basic_string<char, std::char_traits<char>, std::allocator<char> > const&)
読んでみると、配分だけが理由ではないようです。