linux編程的108種奇淫巧計-15(減少複製)

來源:互聯網
上載者:User

      電腦的儲存結構是層次性的,從快到慢,代價從高到低,容量從小到大,寄存器,L1 cache,L2 cache,直到磁碟,甚至比磁碟更慢速的磁帶機,因此在程式運行時,不可避免的會有複製,這一點很重要,從最佳化的角度看,很多時候都是為了減少複製的代價,比如如果已知磁碟的讀寫是順序的,這樣採用DIRECTIO是較好的,直接讀到使用者記憶體上,而不需要先讀到核心記憶體上,然後再複製到使用者記憶體,這個例子我打算在未來的實驗中給出,當然DirectIO相當於程式員自己實現緩衝,在小規模資料讀寫上並沒有優勢,因為節省的這些複製開銷微乎其微。

      本文通過減少cache line回填(也是一種複製),來說明減少複製獲得的收益。首先瞭解一些背景知識:

      CPU通過晶片緩衝(L1 Cache)和記憶體進行資料互動。而互動的單位叫做cache line,大小為2的冪,32或64位元組,虛擬記憶體頁面大小為4KB。cache line的互動分為兩種回填(refill)和回寫(write-back)兩種。假定CPU需要從虛擬記憶體讀取一個位元組的運算元,其地址為0xFFFFFFA3,cache line的大小為32位元組,則CPU需要將地址0xFFFFFFA0地址開始的32個位元組全部讀入,填充出一個完整的cache line後,然後從該cache line的第4個位元組處取得該位元組的內容。如果後繼的指令也需要同樣從cache行中讀,那麼填充cache
line是值得的;否則,額外的填充cache line的時間就是浪費。因此指令和資料的局部性越好,越符合cache line的設計要求。
      我們都非常熟悉的memset函數,如果我們手寫一個memset可能不如庫函數memset的實現效能高,為什麼呢?其中一個主要最佳化技術稱之為non-temporal,其基本思想是,如果要寫入的記憶體資料無用時,直接寫入,而不需要回填cache line。涉及的指令包括movnti,movntdq,sfence等。通俗點說,我們要將一個字元(char),memset在一片記憶體上,而這片記憶體上原有的資料顯然沒有用了,即不需要將這片資料的內容先refill進cacheline,在cacheline中改寫瞭然後再寫回記憶體,只需要直接將資料寫入記憶體即可。庫函數的memset使用的是通用寄存器,而不是SSE寄存器,使用了movnti指令,通過objdump指令可以將庫函數的memset代碼匯出,參見在本文的最後。

      為什麼一定要refill呢,不refill不可以嗎?假定我們對一片記憶體寫入1個位元組(假定一條cache line是32位元組),資料互動的單位是cache Line,系統怎麼知道另外的31個位元組是什麼呢?這一寫回,這1個位元組是對的,另外的31個位元組就未定義了。因此小資料的讀寫refill是有必要的,但是,對於大片記憶體的寫入,顯然refill是可以避免的,intel提供的non-temperal方法也就是為這個目的服務的,即本文的減少複製的最佳化思想。

 

       代碼中還有一些技巧不再詳述,如果有反饋,我再寫個續篇解答,詳細請參見下列代碼。

 

#include <stdlib.h><br />#include <string.h><br />#include <stdio.h><br />#include <iostream><br />typedef unsigned char __attribute__((aligned(16))) fill_t[16];<br />using namespace std;<br />void naive_memset(void *page,unsigned char fill, size_t count)<br />{<br /> unsigned char *dst = (unsigned char*)page;<br /> unsigned char *end = dst + count;<br /> for(;dst<end;)<br /> {<br /> *dst++ = fill;<br /> };<br />};</p><p>void my_memset(void *page, unsigned char fill, size_t count)<br />{<br /> unsigned char *dst = (unsigned char*)page;<br /> fill_t dfill;<br /> for(size_t i = 0;i<16;)<br /> {<br /> dfill[i++]=fill;<br /> }<br /> __asm__ __volatile__ (<br /> " movdqa (%0),%%xmm0/n"<br /> " movdqa %%xmm0,%%xmm1/n"<br /> " movdqa %%xmm0,%%xmm2/n"<br /> " movdqa %%xmm0,%%xmm3/n"<br /> " movdqa %%xmm0,%%xmm4/n"<br /> " movdqa %%xmm0,%%xmm5/n"<br /> " movdqa %%xmm0,%%xmm6/n"<br /> " movdqa %%xmm0,%%xmm7/n"<br /> :: "r"(dfill)<br /> );<br /> while (((long)dst & 0xF) && (count > 0)) {<br /> *dst++ = fill;<br /> count--;<br /> }<br /> size_t m_loop = count/128;<br /> size_t r = count%128;<br /> for(size_t i=0;i<m_loop;++i)<br /> {<br /> __asm__ (<br /> " movntdq %%xmm0, (%0)/n"<br /> " movntdq %%xmm1, 16(%0)/n"<br /> " movntdq %%xmm2, 32(%0)/n"<br /> " movntdq %%xmm3, 48(%0)/n"<br /> " movntdq %%xmm4, 64(%0)/n"<br /> " movntdq %%xmm5, 80(%0)/n"<br /> " movntdq %%xmm6, 96(%0)/n"<br /> " movntdq %%xmm7, 112(%0)/n"<br /> ::"r" (dst) :"memory" );<br /> dst+=128;<br /> }<br /> for(int i=0;i<r;++i)<br /> {<br /> *dst++ = fill;<br /> }<br /> __asm__ __volatile__ (<br /> " sfence /n "<br /> ::<br /> );<br />};<br />#if defined(__i386__)<br />static __inline__ unsigned long long rdtsc(void)<br />{<br /> unsigned long long int x;<br /> __asm__ volatile ("rdtsc" : "=A" (x));<br /> return x;<br />}<br />#elif defined(__x86_64__)<br />static __inline__ unsigned long long rdtsc(void)<br />{<br /> unsigned hi, lo;<br /> __asm__ __volatile__ ("rdtsc" : "=a"(lo), "=d"(hi));<br /> return ( (unsigned long long)lo)|( ((unsigned long long)hi)<<32 );<br />}<br />#endif<br />int main()<br />{<br /> const size_t s = 40*1024*1024;<br /> void* p = malloc(s);<br /> memset(p,0x0,s);<br /> unsigned long long start = rdtsc();<br /> unsigned char*q=(unsigned char*)p;<br /> #ifdef _NAIVE_MEM<br /> naive_memset(p,0x1,s);<br /> #endif<br /> #ifdef _MY_MEM<br /> my_memset(p,0x1,s);<br /> #endif<br /> #ifdef _MEM<br /> memset(p,0x1,s);<br /> #endif<br /> int sum = 0;<br /> for(int i=0;i<s-1;++i)<br /> {<br /> sum += *q;<br /> ++q;<br /> }<br /> cout<<"sum:"<<sum<<endl;<br /> cout<<"run time:"<<rdtsc()-start<<endl;<br /> free(p);<br /> return 0;<br />}</p><p>

 

       glibc的庫函數memset的彙編代碼:

    0000003fb6279540 <memset>:
  3fb6279540:   48 83 fa 07             cmp    $0x7,%rdx
  3fb6279544:   48 89 f9                  mov    %rdi,%rcx
  3fb6279547:   0f 86 96 00 00 00   jbe    3fb62795e3 <memset+0xa3>
  3fb627954d:   49 b8 01 01 01 01 01    mov    $0x101010101010101,%r8
  3fb6279554:   01 01 01
  3fb6279557:   40 0f b6 c6             movzbl %sil,%eax
  3fb627955b:   4c 0f af c0               imul   %rax,%r8
  3fb627955f:   f7 c7 07 00 00 00     test   $0x7,%edi
  3fb6279565:   74 1a                      je     3fb6279581 <memset+0x41>
  3fb6279567:   66 0f 1f 84 00 00 00    nopw   0x0(%rax,%rax,1)
  3fb627956e:   00 00
  3fb6279570:   40 88 31                mov    %sil,(%rcx)
  3fb6279573:   48 ff ca                  dec    %rdx
  3fb6279576:   48 ff c1                  inc    %rcx
  3fb6279579:   f7 c1 07 00 00 00   test   $0x7,%ecx
  3fb627957f:   75 ef                       jne    3fb6279570 <memset+0x30>
  3fb6279581:   48 89 d0                mov    %rdx,%rax
  pennyliang5:   48 c1 e8 06           shr    $0x6,%rax
  3fb6279588:   74 3e                     je     3fb62795c8 <memset+0x88>
  3fb627958a:   48 81 fa c0 d4 01 00    cmp    $0x1d4c0,%rdx
  3fb6279591:   73 6d                    jae    3fb6279600 <memset+0xc0>
  3fb6279593:   66 0f 1f 44 00 00       nopw   0x0(%rax,%rax,1)
  3fb6279599:   0f 1f 80 00 00 00 00    nopl   0x0(%rax)
  3fb62795a0:   4c 89 01                mov    %r8,(%rcx)
  3fb62795a3:   4c 89 41 08             mov    %r8,0x8(%rcx)
  3fb62795a7:   4c 89 41 10             mov    %r8,0x10(%rcx)
  3fb62795ab:   4c 89 41 18             mov    %r8,0x18(%rcx)
  3fb62795af:   4c 89 41 20             mov    %r8,0x20(%rcx)
  3fb62795b3:   4c 89 41 28             mov    %r8,0x28(%rcx)
  3fb62795b7:   4c 89 41 30             mov    %r8,0x30(%rcx)
  3fb62795bb:   4c 89 41 38             mov    %r8,0x38(%rcx)

  3fb62795bf:   48 83 c1 40             add    $0x40,%rcx
  3fb62795c3:   48 ff c8                dec    %rax
  3fb62795c6:   75 d8                   jne    3fb62795a0 <memset+0x60>
  3fb62795c8:   83 e2 3f                and    $0x3f,%edx
  3fb62795cb:   48 89 d0                mov    %rdx,%rax
  3fb62795ce:   48 c1 e8 03             shr    $0x3,%rax
  3fb62795d2:   74 0c                   je     3fb62795e0 <memset+0xa0>
  3fb62795d4:   4c 89 01                mov    %r8,(%rcx)
  3fb62795d7:   48 83 c1 08             add    $0x8,%rcx
  3fb62795db:   48 ff c8                dec    %rax
  3fb62795de:   75 f4                   jne    3fb62795d4 <memset+0x94>
  3fb62795e0:   83 e2 07                and    $0x7,%edx
  3fb62795e3:   48 85 d2                test   %rdx,%rdx
  3fb62795e6:   74 0b                   je     3fb62795f3 <memset+0xb3>
  3fb62795e8:   40 88 31                mov    %sil,(%rcx)
  3fb62795eb:   48 ff c1                inc    %rcx
  3fb62795ee:   48 ff ca                dec    %rdx
  3fb62795f1:   75 f5                   jne    3fb62795e8 <memset+0xa8>
  3fb62795f3:   48 89 f8                mov    %rdi,%rax
  3fb62795f6:   c3                      retq
  3fb62795f7:   66 0f 1f 84 00 00 00    nopw   0x0(%rax,%rax,1)
  3fb62795fe:   00 00
  3fb6279600:   4c 0f c3 01             movnti %r8,(%rcx)
  3fb6279604:   4c 0f c3 41 08          movnti %r8,0x8(%rcx)
  3fb6279609:   4c 0f c3 41 10          movnti %r8,0x10(%rcx)
  3fb627960e:   4c 0f c3 41 18          movnti %r8,0x18(%rcx)
  3fb6279613:   4c 0f c3 41 20          movnti %r8,0x20(%rcx)
  3fb6279618:   4c 0f c3 41 28          movnti %r8,0x28(%rcx)
  3fb627961d:   4c 0f c3 41 30          movnti %r8,0x30(%rcx)
  3fb6279622:   4c 0f c3 41 38          movnti %r8,0x38(%rcx)
  3fb6279627:   48 83 c1 40             add    $0x40,%rcx
  3fb627962b:   48 ff c8                dec    %rax
  3fb627962e:   75 d0                   jne    3fb6279600 <memset+0xc0>
  3fb6279630:   0f ae f8                sfence

  3fb6279633:   eb 93                   jmp    3fb62795c8 <memset+0x88>
  3fb6279635:   90                      nop
  3fb6279636:   90                      nop
  3fb6279637:   90                      nop
  3fb6279638:   90                      nop
  3fb6279639:   90                      nop
  3fb627963a:   90                      nop
  3fb627963b:   90                      nop
  3fb627963c:   90                      nop
  3fb627963d:   90                      nop
  3fb627963e:   90                      nop
  3fb627963f:   90                      nop

 

關於本部落格的一些後續話題,參見:

http://blog.csdn.net/pennyliang/archive/2011/01/18/6151062.aspx

http://blog.csdn.net/pennyliang/archive/2011/01/20/6154929.aspx

 

相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.