⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 csdn_文档中心_内存拷贝的优化方法.htm

📁 csdn10年中间经典帖子
💻 HTM
📖 第 1 页 / 共 3 页
字号:
                      [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 
                      &lt;stdint.h&gt;<BR>#endif<BR><BR>bool 
                      GetPentiumClockEstimateFromRegistry(uint64_t&amp; 
                      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, &amp;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)(&amp;freq_mhz), &amp;cbBuffer);<BR><BR>if (rc == 
                      ERROR_SUCCESS)<BR>frequency = freq_mhz * 
                      MEGA;<BR><BR>RegCloseKey (hKey);<BR>}<BR><BR>return 
                      frequency &gt; 0;<BR>}<BR><BR>void 
                      getTimeStamp(uint64_t&amp; 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> &nbsp;&nbsp;评论人:xyj0323 
      &nbsp;&nbsp;评论:<BR>&nbsp;&nbsp;<TEXTAREA cols=104 name=Critique_Content rows=8></TEXTAREA><BR>&nbsp;&nbsp;<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 &copy; 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 + -