文字列を連結するこれら2つの方法に出くわしました。
共通部分:
char* first= "First";
char* second = "Second";
char* both = malloc(strlen(first) + strlen(second) + 2);
方法1:
strcpy(both, first);
strcat(both, " "); // or space could have been part of one of the strings
strcat(both, second);
方法2:
sprintf(both, "%s %s", first, second);
どちらの場合も、both
のコンテンツは"First Second"
。
どちらがより効率的か(いくつかの連結操作を実行する必要があります)、またはそれを行うためのより良い方法を知っているかどうかを知りたいです。
読みやすくするために、
_char * s = malloc(snprintf(NULL, 0, "%s %s", first, second) + 1);
sprintf(s, "%s %s", first, second);
_
プラットフォームがGNU拡張機能をサポートしている場合、asprintf()
も使用できます。
_char * s = NULL;
asprintf(&s, "%s %s", first, second);
_
MS Cランタイムにこだわっている場合は、_scprintf()
を使用して、結果の文字列の長さを決定する必要があります。
_char * s = malloc(_scprintf("%s %s", first, second) + 1);
sprintf(s, "%s %s", first, second);
_
以下は、おそらく最速のソリューションです。
_size_t len1 = strlen(first);
size_t len2 = strlen(second);
char * s = malloc(len1 + len2 + 2);
memcpy(s, first, len1);
s[len1] = ' ';
memcpy(s + len1 + 1, second, len2 + 1); // includes terminating null
_
効率について心配する必要はありません。コードを読みやすく保守しやすくします。これらの方法の違いがプログラムで重要になるとは思わない。
ここにあなたのためのいくつかの狂気があります、私は実際に行ってそれを測定しました。血まみれの地獄、それを想像してください。意味のある結果が得られたと思います。
「gcc foo.c -o foo.exe -std = c99 -Wall -O2」でビルドした、mingw gcc 4.4を使用して、Windowsを実行するデュアルコアP4を使用しました。
元の投稿の方法1と方法2をテストしました。当初、ベンチマークループの外側でmallocを保持していました。方法1は方法2の48倍高速でした。奇妙なことに、ビルドコマンドから-O2を削除すると、結果のexeが30%高速になりました(まだ調査していません)。
次に、ループ内でmallocとfreeを追加しました。これにより、方法1が4.4倍遅くなりました。方法2は1.1倍遅くなりました。
そのため、malloc + strlen + freeは、sprintfを避ける価値があるプロファイルを十分に支配しないでください。
私が使用したコードは次のとおりです(ループは別として!=の代わりに<を使用して実装されましたが、この投稿のHTMLレンダリングを中断しました)。
void a(char *first, char *second, char *both)
{
for (int i = 0; i != 1000000 * 48; i++)
{
strcpy(both, first);
strcat(both, " ");
strcat(both, second);
}
}
void b(char *first, char *second, char *both)
{
for (int i = 0; i != 1000000 * 1; i++)
sprintf(both, "%s %s", first, second);
}
int main(void)
{
char* first= "First";
char* second = "Second";
char* both = (char*) malloc((strlen(first) + strlen(second) + 2) * sizeof(char));
// Takes 3.7 sec with optimisations, 2.7 sec WITHOUT optimisations!
a(first, second, both);
// Takes 3.7 sec with or without optimisations
//b(first, second, both);
return 0;
}
size_t lf = strlen(first);
size_t ls = strlen(second);
char *both = (char*) malloc((lf + ls + 2) * sizeof(char));
strcpy(both, first);
both[lf] = ' ';
strcpy(&both[lf+1], second);
違いは重要ではありません:
他のポスターが言及したように、これは時期尚早な最適化です。アルゴリズムの設計に専念し、プロファイリングがパフォーマンスの問題であることが判明した場合にのみ、これに戻ります。
とはいえ... I 疑わしい方法1の方が高速です。 sprintf format-stringを解析するためのオーバーヘッドが、確かに小さなものがいくつかあります。そして、strcatは「インライン可能」である可能性が高くなります。
それらはほとんど同じであるはずです。違いは重要ではありません。必要なコードが少ないため、sprintf
を使用します。
sprintf()は単なる文字列よりもはるかに多くを処理するように設計されており、strcat()はスペシャリストです。しかし、私はあなたが小さなものを汗をかいていると思われます。 C文字列は、これら2つの提案された方法の違いをわずかなものにするという点で、根本的に非効率的です。厄介な詳細については、Joel Spolskyによる "Back to Basics" を読んでください。
これは、一般にC++がCよりもパフォーマンスが高いインスタンスです。std:: stringを使用した重い文字列の処理は、より効率的で、確実に安全です。
[編集]
[2回目の編集]修正されたコード(C文字列実装での反復が多すぎる)、タイミング、および結論の変更
Andrew Bainbridgeのstd :: stringの方が遅いというコメントには驚きましたが、このテストケースの完全なコードは投稿しませんでした。私は彼を修正し(タイミングを自動化)、std :: stringテストを追加しました。テストは、デフォルトの「リリース」オプション(最適化)、Athlonデュアルコア、2.6 GHzを備えたVC++ 2008(ネイティブコード)で行われました。結果:
C string handling = 0.023000 seconds
sprintf = 0.313000 seconds
std::string = 0.500000 seconds
したがって、ここではstrcat()はCの文字列規則に固有の非効率性にもかかわらず、はるかに高速です(コンパイラとオプションに応じて走行距離が異なる場合があります)。 。ただし、読み取り可能性と安全性が圧倒的に低いため、パフォーマンスが重要でない場合は、IMOのメリットはほとんどありません。
また、std :: stringstreamの実装もテストしましたが、これもまたはるかに低速でしたが、複雑な文字列のフォーマットにはまだメリットがあります。
修正されたコードは次のとおりです。
#include <ctime>
#include <cstdio>
#include <cstring>
#include <string>
void a(char *first, char *second, char *both)
{
for (int i = 0; i != 1000000; i++)
{
strcpy(both, first);
strcat(both, " ");
strcat(both, second);
}
}
void b(char *first, char *second, char *both)
{
for (int i = 0; i != 1000000; i++)
sprintf(both, "%s %s", first, second);
}
void c(char *first, char *second, char *both)
{
std::string first_s(first) ;
std::string second_s(second) ;
std::string both_s(second) ;
for (int i = 0; i != 1000000; i++)
both_s = first_s + " " + second_s ;
}
int main(void)
{
char* first= "First";
char* second = "Second";
char* both = (char*) malloc((strlen(first) + strlen(second) + 2) * sizeof(char));
clock_t start ;
start = clock() ;
a(first, second, both);
printf( "C string handling = %f seconds\n", (float)(clock() - start)/CLOCKS_PER_SEC) ;
start = clock() ;
b(first, second, both);
printf( "sprintf = %f seconds\n", (float)(clock() - start)/CLOCKS_PER_SEC) ;
start = clock() ;
c(first, second, both);
printf( "std::string = %f seconds\n", (float)(clock() - start)/CLOCKS_PER_SEC) ;
return 0;
}
ケース2で実際の連結が行われたことを知りません。それらを連続して印刷することは、連結を構成しません。
でも、もっと速くなると教えてください:
1)a)文字列Aを新しいバッファにコピーb)文字列Bをバッファにコピーc)バッファを出力バッファにコピー
または
1)文字列Aを出力バッファbにコピーします。文字列bを出力バッファにコピーします