<p>累积的意见(主要是我的意见)转化为答案:</p>
<ul>
<li>如果使用字符串长度的知识并使用<code>memmove()</code>或{<cd2>}代替<code>strcpy()</code>和{<cd4>},会发生什么情况?(我注意到<code>strcat()</code>可以被<code>strcpy()</code>替换,结果没有差别——检查时间可能很有趣。)另外,您没有包括<code><string.h></code>(或<code><stdio.h></code>),因此您错过了<code><string.h></code>可能提供的任何优化!在</li>
</ul>
<blockquote>
<p><em>Marcus:</em> Yes, <code>memmove()</code> is faster than <code>strcpy()</code> and faster than Python, but why? Does <code>memmove()</code> do a word-width copy at a time?</p>
</blockquote>
<ul>
<li>是的;在一台64位机器上,它可以一次移动64位数据,而不是一次移动8位;一台32位机器,可能一次移动32位。它在每次迭代(count)上也只有一个<s>更简单的测试,而不是(<s>count或是null byte'</s>)“这是空字节吗”。在</li>
</ul>
<blockquote>
<p><em>Marcus:</em> But <code>memmove()</code> is still working well even after I make <code>L=L-13</code>, and <code>sizeof(s)</code> gives out <code>L+1024-13</code>. My machine has a <code>sizeof(int)==4</code>.</p>
</blockquote>
<ul>
<li><code>memmove()</code>的代码是高度优化的汇编程序,可能是内联的(没有函数调用开销,但是对于100KiB的数据,函数调用开销最小)。好处是从更大的移动和更简单的循环条件。在</li>
</ul>
<blockquote>
<p><em>Marcus:</em> So does Python use <code>memmove()</code> as well, or something magic?</p>
</blockquote>
<ul>
<li>我没有看过Python源代码,但实际上可以肯定的是,它跟踪字符串的长度(它们是以null结尾的,但是Python总是知道字符串的活动部分有多长)。知道了这个长度,Python就可以使用<code>memmove()</code>或{<cd2>}(区别在于,即使源和目标重叠,<code>memcpy()</code>也可以正常工作;<code>memcpy()</code>如果它们重叠,就没有义务正确工作)。相对而言,他们不太可能有比<code>memmove/memcpy</code>更快的东西。在</li>
</ul>
<hr/>
<p>我修改了C代码以在我的机器上生成更稳定的计时(macosx10.7.4,8gib1333mhz RAM,2.3ghz英特尔酷睿i7,gcc4.7.1),并比较了<code>strcpy()</code>和{<cd4>}vs<code>memcpy()</code>vs<code>memmove()</code>。请注意,我将循环计数从1000增加到10000,以提高计时的稳定性,并且我将整个测试(所有三种机制)重复10次。可以说,定时循环计数应该再增加5-10倍,以便计时超过一秒钟。在</p>
<pre><code>#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <sys/time.h>
#define L (100*1024)
char s[L+1024];
char c[2*L+1024];
static double time_diff( struct timeval et, struct timeval st )
{
return 1e-6*((et.tv_sec - st.tv_sec)*1000000 + (et.tv_usec - st.tv_usec ));
}
static int foo(void)
{
strcpy(c,s);
strcat(c+L,s);
return 0;
}
static int bar(void)
{
memcpy(c + 0, s, L);
memcpy(c + L, s, L);
return 0;
}
static int baz(void)
{
memmove(c + 0, s, L);
memmove(c + L, s, L);
return 0;
}
static void timer(void)
{
struct timeval st;
struct timeval et;
int i;
memset(s, '1', L);
foo();
gettimeofday(&st,NULL);
for( i = 0 ; i < 10000; i++ )
foo();
gettimeofday(&et,NULL);
printf("foo: %f\n", time_diff(et,st));
gettimeofday(&st,NULL);
for( i = 0 ; i < 10000; i++ )
bar();
gettimeofday(&et,NULL);
printf("bar: %f\n", time_diff(et,st));
gettimeofday(&st,NULL);
for( i = 0 ; i < 10000; i++ )
baz();
gettimeofday(&et,NULL);
printf("baz: %f\n", time_diff(et,st));
}
int main(void)
{
for (int i = 0; i < 10; i++)
timer();
return 0;
}
</code></pre>
<p>编译时不会发出警告:</p>
^{pr2}$
<p>我得到的时机是:</p>
<pre><code>foo: 1.781506
bar: 0.155201
baz: 0.144501
foo: 1.276882
bar: 0.187883
baz: 0.191538
foo: 1.090962
bar: 0.179188
baz: 0.183671
foo: 1.898331
bar: 0.142374
baz: 0.140329
foo: 1.516326
bar: 0.146018
baz: 0.144458
foo: 1.245074
bar: 0.180004
baz: 0.181697
foo: 1.635782
bar: 0.136308
baz: 0.139375
foo: 1.542530
bar: 0.138344
baz: 0.136546
foo: 1.646373
bar: 0.185739
baz: 0.194672
foo: 1.284208
bar: 0.145161
baz: 0.205196
</code></pre>
<p>奇怪的是,如果我忽略了“no warnings”而忽略了<code><string.h></code>和{<cd8>}头,就像在最初发布的代码中一样,我得到的计时是:</p>
<pre><code>foo: 1.432378
bar: 0.123245
baz: 0.120716
foo: 1.149614
bar: 0.186661
baz: 0.204024
foo: 1.529690
bar: 0.104873
baz: 0.105964
foo: 1.356727
bar: 0.150993
baz: 0.135393
foo: 0.945457
bar: 0.173606
baz: 0.170719
foo: 1.768005
bar: 0.136830
baz: 0.124262
foo: 1.457069
bar: 0.130019
baz: 0.126566
foo: 1.084092
bar: 0.173160
baz: 0.189040
foo: 1.742892
bar: 0.120824
baz: 0.124772
foo: 1.465636
bar: 0.136625
baz: 0.139923
</code></pre>
<p>仔细观察这些结果,它似乎比“更干净”的代码要快,尽管我没有对这两组数据进行学生t检验,而且计时具有非常大的可变性(但我确实有类似Boinc在后台运行8个进程的东西)。在代码的早期版本中,效果似乎更为明显,当时只测试了<code>strcpy()</code>和{<cd4>}。如果这是真的效果,我无法解释!在</p>
<p><strong>由<a href="https://stackoverflow.com/users/371793/mvds">mvds</a>跟进</strong></p>
<p>既然问题已经结束了,我就不能正确回答了。在Mac电脑上,几乎什么都不做,我得到了以下时间安排:</p>
<p>(带标题)</p>
<pre><code>foo: 1.694667 bar: 0.300041 baz: 0.301693
foo: 1.696361 bar: 0.305267 baz: 0.298918
foo: 1.708898 bar: 0.299006 baz: 0.299327
foo: 1.696909 bar: 0.299919 baz: 0.300499
foo: 1.696582 bar: 0.300021 baz: 0.299775
</code></pre>
<p>(无标题,忽略警告)</p>
<pre><code>foo: 1.185880 bar: 0.300287 baz: 0.300483
foo: 1.120522 bar: 0.299585 baz: 0.301144
foo: 1.122017 bar: 0.299476 baz: 0.299724
foo: 1.124904 bar: 0.301635 baz: 0.300230
foo: 1.120719 bar: 0.300118 baz: 0.299673
</code></pre>
<p>预处理器输出(<code>-E</code>标志)显示,包含头会将<code>strcpy</code>转换为如下内置调用:</p>
<pre><code>((__builtin_object_size (c, 0) != (size_t) -1) ? __builtin___strcpy_chk (c, s, __builtin_object_size (c, 2 > 1)) : __inline_strcpy_chk (c, s));
((__builtin_object_size (c+(100*1024), 0) != (size_t) -1) ? __builtin___strcat_chk (c+(100*1024), s, __builtin_object_size (c+(100*1024), 2 > 1)) : __inline_strcat_chk (c+(100*1024), s));
</code></pre>
<p>因此strcpy的libc版本的性能优于gcc内置版本。(使用<code>gdb</code>可以很容易地验证<code>strcpy</code>上的断点在<code>strcpy()</code>调用中确实没有中断,如果包含头的话)</p>
<p>在Linux(Debian5.0.9,amd64)上,这些差异似乎可以忽略不计。生成的程序集(<code>-S</code>标志)只在include携带的调试信息上有所不同。在</p>