文字列コピー関数memcpy()、strncpy()、snprintf()のパフォーマンスの比較

3661 ワード

質問:
関数memcpy(dest,src,sizeof(dest))、strncpy(dest,src,sizeof(dest))、snprintf(dest,sizeof(dest),“%s”,src)は、src文字列の内容をdest文字列にコピーできます.
どの方式が最も効率的ですか?
つまり、どの方式が一番性能がいいのでしょうか.
解決策:
1.3つのファイルtest_を作成するmemcpy.c,test_strncpy.cとtest_snprintf.c:
ファイルtest_memcpy.c:
david@u1110-hp:~/wrk/tmp/cstring$ cat test_memcpy.c
#include <string.h>
int main(){
	char src[] = "1234567890";
	char dest[2048];
	int len = 0;

	for(int i = 0; i < 10000000; ++i){
		memset(dest, 0, sizeof(dest));
		len = strlen(src);
		len = sizeof(dest) - 1 > len? len: sizeof(dest) -1;
		memcpy(dest, src, len);
		dest[len] = '\0';
	}

	return 0;
}

ファイルtest_strncpy.c:
#include <string.h>
int main() {
	char src[] = "1234567890";
	char dest[2048];
	int len = 0;
	
	for(int i = 0; i < 10000000; ++i) {
		memset(dest, 0, sizeof(dest));
		strncpy(dest, src, sizeof(dest));
	}
	
	return 0;
}
ファイルtest_snprintf.c:
#include <stdio.h>
#include <string.h>

int main() {
	char src[] = "1234567890";
	char dest[2048];
	int len = 0;
	
	for(int i = 0; i < 10000000; ++i) {
		memset(dest, 0, sizeof(dest));
		snprintf(dest, sizeof(dest), "%s", src);
	}
	
	return 0;
}

2.3つのファイルをそれぞれコンパイルする:
david@u1110-hp:~/wrk/tmp/cstring$ gcc -std=c99 -o test_memcpy test_memcpy.c
david@u1110-hp:~/wrk/tmp/cstring$ gcc -std=c99 -o test_strncpy test_strncpy.c
david@u1110-hp:~/wrk/tmp/cstring$ gcc -std=c99 -o test_snprintf test_snprintf.c

3.最適化されていない場合の異なる関数の消費時間の比較:
david@u1110-hp:~/wrk/tmp/cstring$ time ./test_strncpy

real	0m16.472s
user	0m16.309s
sys	0m0.036s
david@u1110-hp:~/wrk/tmp/cstring$ time ./test_snprintf 

real	0m6.106s
user	0m6.100s
sys	0m0.000s
david@u1110-hp:~/wrk/tmp/cstring$ time ./test_memcpy 

real	0m4.179s
user	0m4.144s
sys	0m0.000s
david@u1110-hp:~/wrk/tmp/cstring$

上記の実行結果から、最適化がない場合、memcpy()とstrncpy()の性能は4倍、snprintf()とstrncpy()の性能は約2.5倍異なることがわかります.
4.O 3最適化を採用した場合の異なる関数消費時間の比較:
david@u1110-hp:~/wrk/tmp/cstring$ gcc -std=c99 -O3 -o test_snprintf test_snprintf.c
david@u1110-hp:~/wrk/tmp/cstring$ gcc -std=c99 -O3 -o test_strncpy test_strncpy.c
david@u1110-hp:~/wrk/tmp/cstring$ gcc -std=c99 -O3 -o test_memcpy test_memcpy.c
david@u1110-hp:~/wrk/tmp/cstring$
david@u1110-hp:~/wrk/tmp/cstring$ time ./test_strncpy

real	0m16.178s
user	0m16.161s
sys	0m0.000s
david@u1110-hp:~/wrk/tmp/cstring$ time ./test_snprintf 

real	0m6.242s
user	0m6.032s
sys	0m0.056s
david@u1110-hp:~/wrk/tmp/cstring$ time ./test_memcpy 

real	0m3.567s
user	0m3.436s
sys	0m0.012s
david@u1110-hp:~/wrk/tmp/cstring$

以上の実行結果から,O 3最適化によりmemcpy()とstrncpy()の性能差は5倍近く異なり,snprintf()とstrncpy()の性能差はほぼ約2.5倍変わらなかったことが分かった.
5.性能比較結論:
文字列コピー関数を使用する必要がある場合は、strncpy()を使用しないでください.いつでもsnprintf()で代用しますが、memcpy()はパフォーマンスの良い実装です.strlen+memcpyもlinuxカーネルの実装方法です.
6.意外な収穫結論:
上記3つのファイルのmemset()をbzero()で配列のクリア操作を実現するように変更します.
O 3を用いて最適化を行い,3つの関数の消費時間は以下の通りである.
david@u1110-hp:~/wrk/tmp/cstring$ time ./test_strncpy

real	0m14.395s
user	0m13.929s
sys	0m0.092s
david@u1110-hp:~/wrk/tmp/cstring$ time ./test_snprintf 

real	0m3.785s
user	0m3.772s
sys	0m0.000s
david@u1110-hp:~/wrk/tmp/cstring$ time ./test_memcpy 

real	0m1.241s
user	0m1.236s
sys	0m0.004s
david@u1110-hp:~/wrk/tmp/cstring$

結論:memcpy()とstrncpy()の性能差が約12倍に達し、snprintf()とstrncpy()の性能差も約4倍に達した.
ゼロクリア操作では、bzero()はmemset()よりはるかに効率的です.