?? 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 + -