Chinaunix
标题:
字符串copy效率性能对比~
[打印本页]
作者:
wavemoon
时间:
2009-12-07 20:34
标题:
字符串copy效率性能对比~
程序中总难免会将字符串copy来copy去,常见的方法如:strncpy、snprintf、strlen+memmove等。(strcpy、sprintf之流就不讨论了,由于容易引入目标缓冲区溢出、不能有效保证尾部\0等问题,在实际工程项目中很少使用---如果不怕被bs可以尝试下。其他非主流方如bcopy、memccpy也不罗嗦了,华而不实,本质与上述三种方法并无区别。)
之前看过别人的总结,模糊记得在目标缓冲区较大、实际待拷贝字符串较短时,strncpy性能比较烂,后两种性能较好,但strlen+memmove code起来略显麻烦,所以一般推荐采用snprintf的方式进行拷贝。
偶有闲暇,本着事必躬亲的精神,实际测了下,性能对比如下(程序附后):
case 1(待copy字符串与目标缓冲区长度相近):
循环次数:10000 目标缓冲区长度:10240 源字符串长度:10240
耗时: strncpy: 79ms, snprintf: 24ms, strlen+memmove:23ms
【差距明显,但没有数量级上的差别。】
case 2(待copy字符串与目标缓冲区相差较大):
循环次数:10000 目标缓冲区长度:10240 源字符串长度:5120
耗时: strncpy: 48ms, snprintf: 4ms, strlen+memmove:2ms
【已经产生质的差别了,耗时不在一个数量级上】
case 3(看下另一个极端,源串极小的case)
循环次数:1000000 目标缓冲区长度:10240 源字符串长度:16
耗时: strncpy: 4465ms, snprintf: 127ms, strlen+memmove:32ms
【三者已经明显拉开距离了,一目了然】
从case2、case3来看,strncpy对源串长度变化不敏感,这也符合预期---源串不够长时,strncpy会在目标缓冲区后补充0.
snprintf code起来比较简单,效率有保证,在大多数情况下,跟strlen+memmove在一个量级上。
strlen+memmove虽然三个case都效率最好,但要多行code,而且使用strlen函数也有安全顾虑,好像很少看到这样拷贝字符串的。
从三个case来看,无论在性能、编码复杂度上snprintf都完胜strncpy。
因此在实际code中,strncpy往往是比较忌讳的,容易引起性能瓶颈,而且实在找不出不以snprintf替换它的理由。
顺带跑下题,程序中涉及字符串的操作部分,往往是敏感地带,因为稍不留神就会出现缓冲区溢出, 或处理完后目标串不能保证以\0结尾(如果真的引入这种问题,那等着在后面程序core dump、crash吧),名字带‘n‘的函数一般可以防止目标缓冲区写溢出,那是否也可以保证缓冲区以\0结尾呢?汇总如下(其实随便一个讲c lib库函数地方都有说明):
strncpy: 如果src的前n个字节不含NULL字符,则结果不会以NULL字符结束。
如果src的长度小于n个字节,则以NULL填充dest直到复制完n个字节。
所以strncpy完了,尽量自己补\0
snprintf: 始终保证以\0结尾,即最多copy n-1个有效字符。
可放心用
fgets : 最多读入n-1个字符,始终保证以\0结尾,
可放心用。
[/url]
#include "unistd.h"
#include "sys/time.h"
#define LOOP 10000
#define BUF_LEN 10240
#define SOURCE_LEN 16
char dst[BUF_LEN];
char src[SOURCE_LEN];
class RecTime
{
public:
RecTime()
{
gettimeofday(&ts, NULL);
}
~RecTime()
{
gettimeofday(&te, NULL);
fprintf(stdout, "time used: %dms\n",
(te.tv_sec-ts.tv_sec)*1000 + (te.tv_usec-ts.tv_usec)/1000);
}
private:
struct timeval ts,te;
};
void test_strncpy()
{
RecTime _time_record;
for(int i=0; iLOOP; i++)
{
if(dst != strncpy(dst, src, BUF_LEN))
{
fprintf(stderr, "fatal error, strncpy failed.");
exit(1);
}
}
return ;
}
void test_snprintf()
{
RecTime _time_record;
for(int i=0; iLOOP; i++)
{
if(SOURCE_LEN-1 != snprintf(dst, BUF_LEN, "%s", src))
{
fprintf(stderr, "fatal error, snprintf failed.");
exit(1);
}
}
return ;
}
void test_memmove()
{
RecTime _time_record;
for(int i=0; iLOOP; i++)
{
int len = strlen(src);
if(dst != memmove(dst, src, len))
{
fprintf(stderr, "fatal error, snprintf failed.");
exit(1);
}
}
return ;
}
int main(int argc, char* argv[])
{
/**
memset(src, '1', SOURCE_LEN-1);
src[SOURCE_LEN-1]=0;
/**
test_strncpy();
test_snprintf();
test_memmove();
return 0;
}
本文来自ChinaUnix博客,如果查看原文请点:
[url]http://blog.chinaunix.net/u/23273/showart_2113165.html
欢迎光临 Chinaunix (http://bbs.chinaunix.net/)
Powered by Discuz! X3.2