代码放在这里:
————————————-
其中strlenBSD为delphij实现的FreeBSD使用的strlen标准库函数。http://www.freebsd.org/cgi/cvsweb.cgi/src/lib/libc/string/strlen.c?rev=1.10
strlenVC来自VS2010的CRT src,我建立了一个内嵌汇编函数,其实是有问题的,因为MSDN说的很清楚,内嵌汇编这种方式是没法做优化的。
strlenDiet这个函数来自DietlibC,使用的是0.32版本。
strlenStandard是最一般常见的实现方式,比如wiki上、《c标准库》上都是类似写法。
logger是我自己写的一个高精度计时函数,里面用的QueryPerformanceFrequency这个函数,每段strlen计算都是单独start()以及stop(),然后计算运行时间。
还有一个重要的问题就是project setting。
首先我们使用release版本,在VC2010 project property page里面
最后一个问题就是测试字符串,我们使用了windowsUpdate.txt这个文本文件,大概1.2M左右。
当字符串个数为80000时,运行两次,我们得到测试结果如下(时间单位都是us):
Result strlen 0.000000 ##
Result strlenBSD 0.102586 ##
Result strlenVC 3012.946893 ##
Result strlenDiet 2802.122562 ##
Result strlenStandard 3485.999846 ##
—– second time ——–
Result strlen 0.000000 ##
Result strlenBSD 0.000000 ##
Result strlenVC 3015.086831 ##
Result strlenDiet 2806.302525 ##
Result strlenStandard 3676.002553 ##
可以看到这时候使用CRT标准库的strlen最快,而strlenBSD也是非常非常快乐,而标准实现是最慢的。
去掉优化看看。
Result strlen 4161.887115 ##
Result strlenBSD 3968.818857 ##
Result strlenVC 3078.673800 ##
Result strlenDiet 3819.303217 ##
Result strlenStandard 5035.940199 ##
—————– second ————
Result strlen 3165.460250 ##
Result strlenBSD 4013.348883 ##
Result strlenVC 3764.022041 ##
Result strlenDiet 5623.339493 ##
Result strlenStandard 6379.640180 ##
这个结果就比较有意思了,除了标准实现方式的版本最慢,其他差别不是很大,相比来说VC++内嵌汇编的实现比较快一些。
第二个测试文本来自ironruby的changelog.txt,大概5000行左右,我们依然试图读取80000个字符串(也就是只改变了代码中fopen的源文件)。
全优化版本两次测试结果如下:
Result strlen 0.000000 ##
Result strlenBSD 0.433735 ##
Result strlenVC 1011.775006 ##
Result strlenDiet 826.736862 ##
Result strlenStandard 1050.510424 ##
—————————-
Result strlen 0.000000 ##
Result strlenBSD 0.165407 ##
Result strlenVC 1030.929523 ##
Result strlenDiet 836.285049 ##
Result strlenStandard 1057.499975 ##
无优化设置版本两次测试结果如下:
Result strlen 1166.623783 ##
Result strlenBSD 1438.473774 ##
Result strlenVC 1265.279138 ##
Result strlenDiet 1456.994396 ##
Result strlenStandard 1546.674976 ##
—————————-
Result strlen 1166.989685 ##
Result strlenBSD 1480.218605 ##
Result strlenVC 1033.686648 ##
Result strlenDiet 1239.002409 ##
Result strlenStandard 1450.543506 ##
得到什么结论呢?除了我比较无聊,好像没有什么科学性的结论。如果硬要想,可以说两个:一个是内嵌汇编未必很快,因为编译器没法做优化。另外是,如果你想写一个优化版本strlen,delphij实现的FreeBSD版本是非常好的例子,而dietlibc的实现几乎没有什么改进,反而因为代码复杂容易引入bug。
有几个问题其实可以好好研究的,比如为何delphij的版本会如此快?VC++以及GCC优化选项哪个比较有用?该如何写一个类似CRT这样的strlen(也就是汇编代码编译为so或者lib然后连接进来)。只是这些话题有些超出我水平,就不乱说了。
updated:
新测试代码在这里,增加了一个没对齐情况的测试(p+1),另外测试字符串个数增加到300000,对testbyte,增加一个版本,直接写testbyte在代码里。
测试结果如下:
———–对齐———————–
Result strlenVC 84904.335012 ##
Result strlenDiet 82614.256452 ##
Result strlenStandard 99900.620863 ##
Result strlen 0.000000 ##
Result strlenBSD 0.002673 ##
Result strlenBSD2 0.032413 ##
————未对齐————————
Result strlenVC 87593.624963 ##
Result strlenDiet 83682.625534 ##
Result strlenStandard 99746.446413 ##
Result strlen 0.002339 ##
Result strlenBSD 0.002673 ##
Result strlenBSD2 0.015037 ##
可以看到对齐没对齐差别不是很大,至于0.000000,那是因为太快了,计时器没法计算出差别。
《 “C语言strlen实现之不科学测试” 》 有 4 条评论
…
delphij版本的内联了那个读取byte的..
那个不是快的真正原因。
我觉得那个跟你推荐的libc的strcpy的算法没本质区别,就是把四字节(或者八字节)的循环操作展开了而已
楼主有没有试试SSE 4.2的新text/string/xml SIMD指令?
参考:
http://www.strchr.com/strcmp_and_strlen_using_sse_4.2