📄 csdn_文档中心_内存拷贝的优化方法.htm
字号:
[edi+ecx*8+16], mm2<BR>movntq qword [edi+ecx*8+24],
mm3<BR>movntq qword [edi+ecx*8+32], mm4<BR>movntq qword
[edi+ecx*8+40], mm5<BR>movntq qword [edi+ecx*8+48],
mm6<BR>movntq qword [edi+ecx*8+56], mm7<BR><BR>add ecx,
8<BR>jnz .copyloop<BR><BR>sfence ; flush write
buffer<BR>emms<BR><BR>pop edi<BR>pop
esi<BR><BR>ret<BR></BLOCKQUOTE></TD></TR></TBODY></TABLE></BLOCKQUOTE>
<P><BR><BR>写内存的movq指令全部改为movntq指令,并在复制操作完成后,调用sfence刷新写缓存,因为缓存中内容可能已经失效了。这样一来在写内存外的载入缓存操作,以及缓存本身的操作都被省去,大大减少了冗余内存操作。按AMD的说法能有60%的性能提升,我实测也有50%左右明显的性能提升。<BR>movntq和sfence等指令可以参考Intel的指令手册:<BR><BR><A
href="http://developer.intel.com/design/pentium4/manuals/253666.htm"
target=_blank>The IA-32 Intel Architecture Software Developer's
Manual, Volume 2A: Instruction Set Reference, A-M</A><BR><A
href="http://developer.intel.com/design/pentium4/manuals/253667.htm"
target=_blank>The IA-32 Intel Architecture Software Developer's
Manual, Volume 2B: Instruction Set Reference,
N-Z</A><BR><BR><BR>在优化完写内存后,同样可以通过对读内存的操作进行优化提升性能。虽然CPU在读取数据时,会有一个自动的预读优化,但在操作连续内存区域时显式要求CPU预读数据,还是可以明显地优化性能。<BR></P>
<BLOCKQUOTE>
<TABLE border=0 cellPadding=6 cellSpacing=0 class=ubb_quote
width="100%">
<TBODY>
<TR>
<TD><B>以下为引用:</B>
<BLOCKQUOTE><BR>global _fast_memcpy8<BR><BR>%define param
esp+8+4<BR>%define src param+0<BR>%define dst
param+4<BR>%define len
param+8<BR><BR>_fast_memcpy8:<BR>push esi<BR>push
edi<BR><BR>mov esi, [src] ; source array<BR>mov edi, [dst]
; destination array<BR>mov ecx, [len] ; number of QWORDS
(8 bytes) assumes len / CACHEBLOCK is an integer<BR>shr
ecx, 3<BR><BR>lea esi, [esi+ecx*8] ; end of source<BR>lea
edi, [edi+ecx*8] ; end of destination<BR>neg ecx ; use a
negative offset as a combo
pointer-and-loop-counter<BR><BR>.writeloop:<BR>prefetchnta
[esi+ecx*8 + 512] ; fetch ahead by 512 bytes<BR><BR>movq
mm0, qword [esi+ecx*8]<BR>movq mm1, qword
[esi+ecx*8+8]<BR>movq mm2, qword [esi+ecx*8+16]<BR>movq
mm3, qword [esi+ecx*8+24]<BR>movq mm4, qword
[esi+ecx*8+32]<BR>movq mm5, qword [esi+ecx*8+40]<BR>movq
mm6, qword [esi+ecx*8+48]<BR>movq mm7, qword
[esi+ecx*8+56]<BR><BR>movntq qword [edi+ecx*8],
mm0<BR>movntq qword [edi+ecx*8+8], mm1<BR>movntq qword
[edi+ecx*8+16], mm2<BR>movntq qword [edi+ecx*8+24],
mm3<BR>movntq qword [edi+ecx*8+32], mm4<BR>movntq qword
[edi+ecx*8+40], mm5<BR>movntq qword [edi+ecx*8+48],
mm6<BR>movntq qword [edi+ecx*8+56], mm7<BR><BR>add ecx,
8<BR>jnz .writeloop<BR><BR>sfence ; flush write
buffer<BR>emms<BR><BR>pop edi<BR>pop
esi<BR><BR>ret<BR></BLOCKQUOTE></TD></TR></TBODY></TABLE></BLOCKQUOTE>
<P><BR><BR>增加一个简单的prefetchnta指令,提示CPU在处理当前读取内存操作的同时,预读前面512字节处的一个缓存行64字节内容。这样一来又可以有10%左右的性能提升。<BR>最后,对正在处理的内存,可以通过显式的内存读取操作,强制性要求其载入到缓存中,因为prefetchnta指令还只是一个提示,可以被CPU忽略。这样可以再次获得60%左右的性能提示,我实测没有这么高,但是也比较明显。<BR></P>
<BLOCKQUOTE>
<TABLE border=0 cellPadding=6 cellSpacing=0 class=ubb_quote
width="100%">
<TBODY>
<TR>
<TD><B>以下为引用:</B>
<BLOCKQUOTE><BR>global _fast_memcpy9<BR><BR>%define param
esp+12+4<BR>%define src param+0<BR>%define dst
param+4<BR>%define len param+8<BR><BR>%define CACHEBLOCK
400h<BR><BR>_fast_memcpy9:<BR>push esi<BR>push edi<BR>push
ebx<BR><BR>mov esi, [src] ; source array<BR>mov edi, [dst]
; destination array<BR>mov ecx, [len] ; number of QWORDS
(8 bytes) assumes len / CACHEBLOCK is an integer<BR>shr
ecx, 3<BR><BR>lea esi, [esi+ecx*8] ; end of source<BR>lea
edi, [edi+ecx*8] ; end of destination<BR>neg ecx ; use a
negative offset as a combo
pointer-and-loop-counter<BR><BR>.mainloop:<BR>mov eax,
CACHEBLOCK / 16 ; note: .prefetchloop is unrolled
2X<BR>add ecx, CACHEBLOCK ; move up to end of
block<BR><BR>.prefetchloop:<BR>mov ebx, [esi+ecx*8-64] ;
read one address in this cache line...<BR>mov ebx,
[esi+ecx*8-128] ; ... and one in the previous line<BR>sub
ecx, 16 ; 16 QWORDS = 2 64-byte cache lines<BR>dec
eax<BR>jnz .prefetchloop<BR><BR>mov eax, CACHEBLOCK /
8<BR><BR>.writeloop:<BR>prefetchnta [esi+ecx*8 + 512] ;
fetch ahead by 512 bytes<BR><BR>movq mm0, qword
[esi+ecx*8]<BR>movq mm1, qword [esi+ecx*8+8]<BR>movq mm2,
qword [esi+ecx*8+16]<BR>movq mm3, qword
[esi+ecx*8+24]<BR>movq mm4, qword [esi+ecx*8+32]<BR>movq
mm5, qword [esi+ecx*8+40]<BR>movq mm6, qword
[esi+ecx*8+48]<BR>movq mm7, qword
[esi+ecx*8+56]<BR><BR>movntq qword [edi+ecx*8],
mm0<BR>movntq qword [edi+ecx*8+8], mm1<BR>movntq qword
[edi+ecx*8+16], mm2<BR>movntq qword [edi+ecx*8+24],
mm3<BR>movntq qword [edi+ecx*8+32], mm4<BR>movntq qword
[edi+ecx*8+40], mm5<BR>movntq qword [edi+ecx*8+48],
mm6<BR>movntq qword [edi+ecx*8+56], mm7<BR><BR>add ecx,
8<BR>dec eax<BR>jnz .writeloop<BR><BR>or ecx, ecx ;
assumes integer number of cacheblocks<BR>jnz
.mainloop<BR><BR>sfence ; flush write
buffer<BR>emms<BR><BR>pop ebx<BR>pop edi<BR>pop
esi<BR><BR>ret<BR></BLOCKQUOTE></TD></TR></TBODY></TABLE></BLOCKQUOTE>
<P><BR><BR>至此,一个完整的内存复制函数的优化流程就结束了,通过对缓存的了解和使用,一次又一次地超越自己,最终获得一个较为令人满意地结果。(号称300%性能提示,实测175%-200%,也算相当不错了)<BR><BR>在编写测试代码的时候需要注意两点:<BR><BR>一是计时精度的问题,需要使用高精度的物理计数器,避免误差。推荐使用rdtsc指令,然后根据CPU主频计算时间。CPU主频可以通过高精度计时器动态计算,我这儿偷懒直接从注册表里面读取了
:P<BR>代码如下:<BR></P>
<BLOCKQUOTE>
<TABLE border=0 cellPadding=6 cellSpacing=0 class=ubb_quote
width="100%">
<TBODY>
<TR>
<TD><B>以下为引用:</B>
<BLOCKQUOTE><BR>#ifdef WIN32<BR>typedef __int64
uint64_t;<BR>#else<BR>#include
<stdint.h><BR>#endif<BR><BR>bool
GetPentiumClockEstimateFromRegistry(uint64_t&
frequency)<BR>{<BR>HKEY hKey;<BR><BR>frequency =
0;<BR><BR>LONG rc = ::RegOpenKeyEx(HKEY_LOCAL_MACHINE,
"Hardware\\Description\\System\\CentralProcessor\\0", 0,
KEY_READ, &hKey);<BR><BR>if(rc ==
ERROR_SUCCESS)<BR>{<BR>DWORD cbBuffer = sizeof
(DWORD);<BR>DWORD freq_mhz;<BR><BR>rc =
::RegQueryValueEx(hKey, "~MHz", NULL, NULL,
(LPBYTE)(&freq_mhz), &cbBuffer);<BR><BR>if (rc ==
ERROR_SUCCESS)<BR>frequency = freq_mhz *
MEGA;<BR><BR>RegCloseKey (hKey);<BR>}<BR><BR>return
frequency > 0;<BR>}<BR><BR>void
getTimeStamp(uint64_t& timeStamp)<BR>{<BR>#ifdef
WIN32<BR>__asm<BR>{<BR>push edx<BR>push ecx<BR>mov ecx,
timeStamp<BR>//_emit 0Fh // RDTSC<BR>//_emit
31h<BR>rdtsc<BR>mov [ecx], eax<BR>mov [ecx+4], edx<BR>pop
ecx<BR>pop edx<BR>}<BR>#else<BR>__asm__ __volatile__
("rdtsc" : "=A"
(timeStamp));<BR>#endif<BR>}<BR></BLOCKQUOTE></TD></TR></TBODY></TABLE></BLOCKQUOTE>
<P><BR><BR><BR>二是测试内存复制的缓冲区的大小,如果缓冲区过小,第一次拷贝两个缓冲区时就会导致所有数据都被载入L2缓存中,得出比普通内存操作高一个数量级的数值。例如我的L2缓冲为256K,如果我用两个128K的缓冲区对着拷贝,无论循环多少次,速度都在普通内存复制的10倍左右。因此设置一个较大的值是必要的。<BR></P><BR></TD></TR></TBODY></TABLE></TD></TR></TBODY></TABLE><BR>
<TABLE align=center bgColor=#006699 border=0 cellPadding=0 cellSpacing=0
width=770>
<TBODY>
<TR bgColor=#006699>
<TD align=middle bgColor=#006699 id=white><FONT
color=#ffffff>对该文的评论</FONT></TD>
<TD align=middle>
<SCRIPT src="CSDN_文档中心_内存拷贝的优化方法.files/readnum.htm"></SCRIPT>
</TD></TR></TBODY></TABLE><BR>
<DIV align=center>
<TABLE align=center bgColor=#cccccc border=0 cellPadding=2 cellSpacing=1
width=770>
<TBODY>
<TR>
<TH bgColor=#006699 id=white><FONT
color=#ffffff>我要评论</FONT></TH></TR></TBODY></TABLE></DIV>
<SCRIPT language=javascript>
<!--
function isEmpty(s)
{
return ((s == null) || (s.length == 0))
}
function fubmitok()
{
if (isEmpty(document.add_critique.Critique_Content.value))
{
alert('评论不能为空!!!!') ;
return false;
}
document.add_critique.submit();
}
//-->
</SCRIPT>
<DIV align=center>
<TABLE border=0 width=770>
<TBODY>
<TR>
<TD>
<FORM action=Critique_Sql.asp method=post name=add_critique><INPUT
name=Critique_State type=hidden value=add> 评论人:xyj0323
评论:<BR> <TEXTAREA cols=104 name=Critique_Content rows=8></TEXTAREA><BR> <INPUT name=ubmit onclick=javascript:fubmitok(); type=button value=发表评论>
<INPUT name=Topic_id type=hidden value=27092> <INPUT name=From type=hidden
value=/Develop/Build_Article.asp?id=27092>
</FORM></TD></TR></TBODY></TABLE></DIV><BR>
<HR noShade SIZE=1 width=770>
<TABLE border=0 cellPadding=0 cellSpacing=0 width=500>
<TBODY>
<TR align=middle>
<TD height=10 vAlign=bottom><A
href="http://www.csdn.net/intro/intro.asp?id=2">网站简介</A> - <A
href="http://www.csdn.net/intro/intro.asp?id=5">广告服务</A> - <A
href="http://www.csdn.net/map/map.shtm">网站地图</A> - <A
href="http://www.csdn.net/help/help.asp">帮助信息</A> - <A
href="http://www.csdn.net/intro/intro.asp?id=2">联系方式</A> - <A
href="http://www.csdn.net/english">English</A> </TD>
<TD align=middle rowSpan=3><A
href="http://www.hd315.gov.cn/beian/view.asp?bianhao=010202001032100010"><IMG
border=0 height=48 src="CSDN_文档中心_内存拷贝的优化方法.files/biaoshi.gif"
width=40></A></TD></TR>
<TR align=middle>
<TD vAlign=top>百联美达美公司 版权所有 京ICP证020026号</TD></TR>
<TR align=middle>
<TD vAlign=top><FONT face=Verdana>Copyright © CSDN.net, Inc. All rights
reserved</FONT></TD></TR>
<TR>
<TD height=15></TD>
<TD></TD></TR></TBODY></TABLE></DIV>
<DIV></DIV><!--内容结束//--><!--结束//--></BODY></HTML>
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -