分享一下最近做程序优化的一点小心得:在写高并发交易代码时要谨慎使用strncpy和sprintf。
下面详细介绍一下这样说的原因及建议实践:
1 慎用strncpy因为它的副作用极大
我们平时使用strncpy防止字符串拷贝时溢出,常常这样写
char buf[1024] = {0}; char str[16] = "hello"; strncpy(buf, sizefo(buf), str);
这样写当然没问题,但有些人不知道的是:strncpy一行代码执行时是往buf写了sizeof(buf) = 1024个字节,而不是直观以为的strlen(str) + 1 = 6个字符。
也就是说我们为了复制6个字符却写了1024个字节,多了不少额外消耗。如果这个函数被频繁调用,会导致系统性能出现不少损失。
因为调用strncpy(dest, n, str)时,函数首先将字符从源缓冲区str逐个复制到目标缓冲区dest,直到拷贝了n碰上\0。
紧接着,strncpy函数会往buf填充\0字符直到写满n个字符。
所以我才会说上面的代码strncpy才会写了1024个字节。
可以做一个小实验:
看上面代码及输出结果,我们可以知道在执行strncpy之前dest是用'1'填充的,但在执行strncpy后,前面几个字符变成hello,后面的字符全变成\0;
我个人的解决方法是写一个宏专用于往字符数组拷贝的,与大家分享一下,抛砖引玉。
// 静态断言 从vc拷贝过来(_STATIC_ASSERT) 稍微修改了一下 // 原来是typedef char __static_assert_t[ (expr) ] // 现在是typedef char __static_assert_t[ (expr) - 1 ] // 原因是gcc支持0字符数组 //TODO: 这里在win上编译有警告 有待优化 另外在linux宏好像不起作用 原因待查。暂时只有在win编译代码可以用 #ifndef _STATIC_ASSERT_RCC # ifdef __GNUC__ # define _STATIC_ASSERT_RCC(expr) typedef char __static_assert_t[ (expr) - 1 ] # else # define _STATIC_ASSERT_RCC(expr) do { typedef char __static_assert_t[ (expr) ]; } while (0) # endif #endif //将src复制到字符数组arr 保证不会越界并且末尾肯定会加\0 //_STATIC_ASSERT_RCC这里作用是防止有人传字符串指针进来 #define strncpy2arr(arr, src) do { \ char *dest_ = arr; \ size_t n = strnlen(src, sizeof(arr) - 1); \ _STATIC_ASSERT_RCC(sizeof(arr) != sizeof(char *)); \ memcpy(dest_, src, n); \ dest_[n] = '\0'; \ } while (0) #ifdef WIN32 int main(int argc, char *argv[]) { char dest[16]; char *src = "hello 222"; int i = 0; for (i = 0; i < sizeof(dest); ++i) { dest[i] = '1'; } printf("before strncpy\n"); for (i = 0; i < sizeof(dest); ++i) { printf("%d ", dest[i]); } printf("\n"); strncpy2arr(dest, src); printf("after strncpy\n"); for (i = 0; i < sizeof(dest); ++i) { printf("%d ", dest[i]); } printf("\n"); strncpy(dest, src, sizeof(dest)); printf("after strncpy\n"); for (i = 0; i < sizeof(dest); ++i) { printf("%d ", dest[i]); } printf("\n"); return 0; //return CompressPerformanceTestMain(argc, argv); } #endif
2 慎用sprintf,因为它的效率比你想象的低
之前我一直没注意到sprintf效率低的问题,直到有一次使用callgrind对程序进行性能分析时,发现有相当大的资源消耗在sprintf上面,我才有所警觉。
为此,我写了一点测试代码,对常用的函数做了一下基准测试,结果如下:
测试内容 |
耗时(us) |
for循环赋值40亿次 |
13023889 |
调用简单函数40亿次 |
16967986 |
调用memset函数4亿次 (256个字节) |
6932237 |
调用strcpy函数4亿次 (12个字节) |
3239218 |
调用memcpy函数4亿次 (12个字节) |
3239201 |
调用strcmp函数4亿次 (12个字节) |
2500568 |
调用memcmp函数4亿次 (12个字节) |
26683 |
首页 上一页 1 2 3 4 下一页 尾页 1/4/4 | |
【大 中 小】【打印】 【繁体】【投稿】【收藏】 【推荐】【举报】【评论】 【关闭】 【返回顶部】 | |
上一篇:cf121C. Lucky Permutation(康托.. | 下一篇:洛谷P2973 [USACO10HOL]赶小猪(高.. |