为了效率,我们可以用的招数 之 strchr

如果要写一个从字符串中查找一个字符的函数,相信你不难想到如下代码:

 1 char* __cdecl strchr_1(
 2   char const* _Str,
 3   int         _Val
 4 ) {
 5 
 6   while (*_Str && *_Str != _Val)
 7     _Str++;
 8   
 9   if (*_Str == _Val)
10     return(_Str);
11 
12   return NULL;
13 }

 

 strlen 的效率之旅一样,我们先做好测试脚手架:

 1 typedef char* (__cdecl* p_strchr)(
 2   char const* _Str,
 3   int         _Val
 4 );
 5 
 6 // 测试正确性
 7 void test_function(
 8     char* funName,
 9     p_strchr function,
10     char* str,
11     char ch,
12     int expectIndex) {
13   int got = function(str, ch) - (int)str;
14   printf(
15       "func [%s] test value [%s] find char [%c],"
16       " expect [%d], got [%d], %s\n",
17       funName,
18       str,
19       ch,
20       got,
21       expectIndex,
22       got == expectIndex
23           ? "true"
24           : "false"
25   );
26 }
27 
28 // 正确性测试用例
29 void test_functions(char* funName, p_strchr function) {
30   struct  test_item {
31 
32     char* str;
33     char val;
34     int expect_index;
35   } items[] = {
36     // return NULL, expect nagtive address of "a"
37     { "a", 'b',  -(int)"a" }, 
38     // last is '\0', returns 1
39     { "a", '\0', 1 },         
40     { "ab", 'a', 0 },
41     { "ab", 'b', 1 },
42     {"abc", 'b', 1 }
43   };
44 
45   int size = sizeof(items) / sizeof(struct test_item);
46 
47   for (int i = 0; i < size; i++) {
48     test_function(
49         funName,
50         function,
51         items[i].str,
52         items[i].val,
53         items[i].expect_index);
54   }
55 }

 

放入main函数执行,执行结果如下:

 

 

 

可以看到,我们获取到了我们想要的效果,那么接下来再来一个效率对比,我们的测试脚手架如下:

 1 #define BUFF_SIZE 100000000
 2 char buff[BUFF_SIZE + 1]; // 100M + 1BYTE
 3 void test_function_prof(
 4     char* funName,
 5     p_strchr function,
 6     char* str,
 7     char ch) {
 8   ULONGLONG start = 0;
 9   ULONGLONG end = 0;
10 
11   start = GetTickCount64();
12   function(str, ch);
13   end = GetTickCount64();
14 
15   printf(
16       "test func [%s] start [%lld], end [%lld], cost: [%lld]\n",
17       funName,
18       start,
19       end,
20       end - start
21   );
22 }
23 
24 void test_profs() {
25   // init
26   int size = BUFF_SIZE;
27   for (int i = 0; i < size; i++) {
28     buff[i] = 'a';
29   }
30   buff[BUFF_SIZE - 1] = 'b';
31   buff[BUFF_SIZE] = '\0';
32 
33   test_function_prof("strchar_1", strchr_1, buff, 'b');
34 }

在主函数中调用 test_profs 函数,得到结果如下:

 

 

 一个100M长的字符串,查找到结尾需要 156ms,那么系统自带的 strchr 函数表现如何呢?向 test_profs 函数添加如下代码:

1 test_function_prof("strchar",   strchr,   buff, 'b');

得到结果如下:

 

 

 哇,差距挺大的,居然差了8.75(140/16)倍,那么效率何来?我们到源码中找答案,找到系统 strchr 函数的实现,我们获取到如下代码:

  1         page    ,132
  2         title   strchr - search string for given character
  3 ;***
  4 ;strchr.asm - search a string for a given character
  5 ;
  6 ;       Copyright (c) Microsoft Corporation. All rights reserved.
  7 ;
  8 ;Purpose:
  9 ;       defines strchr() - search a string for a character
 10 ;
 11 ;*******************************************************************************
 12 
 13         .xlist
 14         include vcruntime.inc
 15         .list
 16 
 17 page
 18 ;***
 19 ;char *strchr(string, chr) - search a string for a character
 20 ;
 21 ;Purpose:
 22 ;       Searches a string for a given character, which may be the
 23 ;       null character '\0'.
 24 ;
 25 ;       Algorithm:
 26 ;       char *
 27 ;       strchr (string, chr)
 28 ;       char *string, chr;
 29 ;       {
 30 ;         while (*string && *string != chr)
 31 ;             string++;
 32 ;         if (*string == chr)
 33 ;             return(string);
 34 ;         return((char *)0);
 35 ;       }
 36 ;
 37 ;Entry:
 38 ;       char *string - string to search in
 39 ;       char chr     - character to search for
 40 ;
 41 ;Exit:
 42 ;       returns pointer to the first occurence of c in string
 43 ;       returns NULL if chr does not occur in string
 44 ;
 45 ;Uses:
 46 ;
 47 ;Exceptions:
 48 ;
 49 ;*******************************************************************************
 50 
 51         CODESEG
 52 
 53         align   16
 54         public  strchr, __from_strstr_to_strchr
 55 strchr  proc \
 56         string:ptr byte, \
 57         chr:byte
 58 
 59         OPTION PROLOGUE:NONE, EPILOGUE:NONE
 60 
 61         .FPO    ( 0, 2, 0, 0, 0, 0 )
 62 
 63 ; Include SSE2 code path for platforms that support it.
 64         include strchr_sse.inc
 65 
 66         xor     eax,eax
 67         mov     al,[esp + 8]        ; al = chr (search char)
 68 
 69 __from_strstr_to_strchr label proc
 70 
 71         push    ebx                 ; PRESERVE EBX
 72         mov     ebx,eax             ; ebx = 0/0/0/chr
 73         shl     eax,8               ; eax = 0/0/chr/0
 74         mov     edx,[esp + 8]       ; edx = buffer
 75         test    edx,3               ; test if string is aligned on 32 bits
 76         jz      short main_loop_start
 77 
 78 str_misaligned:                     ; simple byte loop until string is aligned
 79         mov     cl,[edx]
 80         add     edx,1
 81         cmp     cl,bl
 82         je      short found_bx
 83         test    cl,cl
 84         jz      short retnull_bx
 85         test    edx,3               ; now aligned ?
 86         jne     short str_misaligned
 87 
 88 main_loop_start:                    ; set all 4 bytes of ebx to [chr]
 89         or      ebx,eax             ; ebx = 0/0/chr/chr
 90         push    edi                 ; PRESERVE EDI
 91         mov     eax,ebx             ; eax = 0/0/chr/chr
 92         shl     ebx,10h             ; ebx = chr/chr/0/0
 93         push    esi                 ; PRESERVE ESI
 94         or      ebx,eax             ; ebx = all 4 bytes = [chr]
 95 
 96 ; in the main loop (below), we are looking for chr or for EOS (end of string)
 97 
 98 main_loop:
 99         mov     ecx,[edx]           ; read  dword (4 bytes)
100         mov     edi,7efefeffh       ; work with edi & ecx for looking for chr
101 
102         mov     eax,ecx             ; eax = dword
103         mov     esi,edi             ; work with esi & eax for looking for EOS
104 
105         xor     ecx,ebx             ; eax = dword xor chr/chr/chr/chr
106         add     esi,eax
107 
108         add     edi,ecx
109         xor     ecx,-1
110 
111         xor     eax,-1
112         xor     ecx,edi
113 
114         xor     eax,esi
115         add     edx,4
116 
117         and     ecx,81010100h       ; test for chr
118         jnz     short chr_is_found  ; chr probably has been found
119 
120         ; chr was not found, check for EOS
121 
122         and     eax,81010100h       ; is any flag set ??
123         jz      short main_loop     ; EOS was not found, go get another dword
124 
125         and     eax,01010100h       ; is it in high byte?
126         jnz     short retnull       ; no, definitely found EOS, return failure
127 
128         and     esi,80000000h       ; check was high byte 0 or 80h
129         jnz     short main_loop     ; it just was 80h in high byte, go get
130                                     ; another dword
131 retnull:
132         pop     esi
133         pop     edi
134 retnull_bx:
135         pop     ebx
136         xor     eax,eax
137         ret                         ; _cdecl return
138 
139 found_bx:
140         lea     eax,[edx - 1]
141         pop     ebx                 ; restore ebx
142         ret                         ; _cdecl return
143 
144 chr_is_found:
145         mov     eax,[edx - 4]       ; let's look one more time on this dword
146         cmp     al,bl               ; is chr in byte 0?
147         je      short byte_0
148         test    al,al               ; test if low byte is 0
149         je      retnull
150         cmp     ah,bl               ; is it byte 1
151         je      short byte_1
152         test    ah,ah               ; found EOS ?
153         je      retnull
154         shr     eax,10h             ; is it byte 2
155         cmp     al,bl
156         je      short byte_2
157         test    al,al               ; if in al some bits were set, bl!=bh
158         je      retnull
159         cmp     ah,bl
160         je      short byte_3
161         test    ah,ah
162         jz      retnull
163         jmp     short main_loop     ; neither chr nor EOS found, go get
164                                     ; another dword
165 byte_3:
166         pop     esi
167         pop     edi
168         lea     eax,[edx - 1]
169         pop     ebx                 ; restore ebx
170         ret                         ; _cdecl return
171 
172 byte_2:
173         lea     eax,[edx - 2]
174         pop     esi
175         pop     edi
176         pop     ebx
177         ret                         ; _cdecl return
178 
179 byte_1:
180         lea     eax,[edx - 3]
181         pop     esi
182         pop     edi
183         pop     ebx
184         ret                         ; _cdecl return
185 
186 byte_0:
187         lea     eax,[edx - 4]
188         pop     esi                 ; restore esi
189         pop     edi                 ; restore edi
190         pop     ebx                 ; restore ebx
191         ret                         ; _cdecl return
192 
193 strchr  endp
194         end

其中,

78-86行进行地址32位对齐(硬件访问32位对齐地址更快);

88-94行我们将ebx(32位4字节)中的四个字节均设置为要查找的字符;

98-129行是对字符串的迭代查找目标字符;

在循环中,我们看到了几个特殊的值,分别列出值以及其二进制位如下:

 

我们先看下对 ecx 寄存器的操作:

  1. 读取查询字符串的四个字节(这里因为是 ASCII 字符,所以是四个字符);
  2. 与 ebx(chr/chr/chr/chr) 异或;
  3. 按位取反(xor ecx, -1);
  4. edi = 0x7efefeff + ecx;
  5. 异或edi;
  6. 按位与 0x81010100;
  7. 跳转到依次判断各个字节是否位目标字符的逻辑;

从以上的逻辑,我们可以看到,117行判断决定了是否有可能找到了目标字符。那么是怎么做到的呢?

首先,我们假设 ecx 所有字节均不是目标值,则执行 xor     ecx,ebx 之后,所有字节均不为0;

第二步,按位取反,则 ecx 所有字节均不为 0;

第三步,edi = 0x7efefeff + ecx,这一步参考 strlen 的效率之旅 中对 '\0' 的判断;

第四步,判断 ecx & 81010100 的值,如果结果不为0,则可能找到了对应字符,跳转到字节判断逻辑(chr_is_found);

第五步,如果走到了122行,则说明肯定没有找到目标,这里判断是否到了查找字符串结尾,逻辑和 strlen 的效率之旅 中判断字符串结尾逻辑相同,每次读取并处理了四个字节。

那么就不难理解,效率肯定比之前单字节匹配的速度更快,但......为什么是 8 倍而不是 4 倍呢?

仔细看代码之后,发现有这么一句:

1 ; Include SSE2 code path for platforms that support it.
2         include strchr_sse.inc

由于没有找到对应源代码,所以这里我们通过反汇编的方式,获取到strchr函数真正执行的代码来看,反汇编结果如下:

  1 cmp         dword ptr ds:[7BEB92DCh],1        ; 7BEA42E0  
  2 jb          7BEA4348                          ; 7BEA42E7  
  3 movzx       eax,byte ptr [esp+8]              ; 7BEA42E9  
  4 mov         edx,eax                           ; 7BEA42EE  
  5 shl         eax,8                             ; 7BEA42F0  
  6 or          edx,eax                           ; 7BEA42F3  
  7 movd        xmm3,edx                          ; 7BEA42F5  
  8 pshuflw     xmm3,xmm3,0                       ; 7BEA42F9  
  9 movlhps     xmm3,xmm3                         ; 7BEA42FE  
 10 mov         edx,dword ptr [esp+4]             ; 7BEA4301  
 11 mov         ecx,0Fh                           ; 7BEA4305  
 12 or          eax,0FFFFFFFFh                    ; 7BEA430A  
 13 and         ecx,edx                           ; 7BEA430D  
 14 shl         eax,cl                            ; 7BEA430F  
 15 sub         edx,ecx                           ; 7BEA4311  
 16 movdqu      xmm1,xmmword ptr [edx]            ; 7BEA4313  
 17 pxor        xmm2,xmm2                         ; 7BEA4317  
 18 pcmpeqb     xmm2,xmm1                         ; 7BEA431B  
 19 pcmpeqb     xmm1,xmm3                         ; 7BEA431F  
 20 por         xmm2,xmm1                         ; 7BEA4323  
 21 pmovmskb    ecx,xmm2                          ; 7BEA4327  
 22 and         ecx,eax                           ; 7BEA432B  
 23 jne         7BEA4337                          ; 7BEA432D  
 24 or          eax,0FFFFFFFFh                    ; 7BEA432F  
 25 add         edx,10h                           ; 7BEA4332  
 26 jmp         7BEA4313                          ; 7BEA4335  
 27 bsf         eax,ecx                           ; 7BEA4337  
 28 add         eax,edx                           ; 7BEA433A  
 29 movd        edx,xmm3                          ; 7BEA433C  
 30 xor         ecx,ecx                           ; 7BEA4340  
 31 cmp         dl,byte ptr [eax]                 ; 7BEA4342  
 32 cmovne      eax,ecx                           ; 7BEA4344  
 33 ret                                           ; 7BEA4347  
 34 xor         eax,eax                           ; 7BEA4348  
 35 mov         al,byte ptr [esp+8]               ; 7BEA434A  
 36 push        ebx                               ; 7BEA434E  
 37 mov         ebx,eax                           ; 7BEA434F  
 38 shl         eax,8                             ; 7BEA4351  
 39 mov         edx,dword ptr [esp+8]             ; 7BEA4354  
 40 test        edx,3                             ; 7BEA4358  
 41 je          7BEA4375                          ; 7BEA435E  
 42 mov         cl,byte ptr [edx]                 ; 7BEA4360  
 43 add         edx,1                             ; 7BEA4362  
 44 cmp         cl,bl                             ; 7BEA4365  
 45 je          7BEA43C2                          ; 7BEA4367  
 46 test        cl,cl                             ; 7BEA4369  
 47 je          7BEA43BE                          ; 7BEA436B  
 48 test        edx,3                             ; 7BEA436D  
 49 jne         7BEA4360                          ; 7BEA4373  
 50 or          ebx,eax                           ; 7BEA4375  
 51 push        edi                               ; 7BEA4377  
 52 mov         eax,ebx                           ; 7BEA4378  
 53 shl         ebx,10h                           ; 7BEA437A  
 54 push        esi                               ; 7BEA437D  
 55 or          ebx,eax                           ; 7BEA437E  
 56 mov         ecx,dword ptr [edx]               ; 7BEA4380  
 57 mov         edi,7EFEFEFFh                     ; 7BEA4382  
 58 mov         eax,ecx                           ; 7BEA4387  
 59 mov         esi,edi                           ; 7BEA4389  
 60 xor         ecx,ebx                           ; 7BEA438B  
 61 add         esi,eax                           ; 7BEA438D  
 62 add         edi,ecx                           ; 7BEA438F  
 63 xor         ecx,0FFFFFFFFh                    ; 7BEA4391  
 64 xor         eax,0FFFFFFFFh                    ; 7BEA4394  
 65 xor         ecx,edi                           ; 7BEA4397  
 66 xor         eax,esi                           ; 7BEA4399  
 67 add         edx,4                             ; 7BEA439B  
 68 and         ecx,81010100h                     ; 7BEA439E  
 69 jne         7BEA43C7                          ; 7BEA43A4  
 70 and         eax,81010100h                     ; 7BEA43A6  
 71 je          7BEA4380                          ; 7BEA43AB  
 72 and         eax,1010100h                      ; 7BEA43AD  
 73 jne         7BEA43BC                          ; 7BEA43B2  
 74 and         esi,80000000h                     ; 7BEA43B4  
 75 jne         7BEA4380                          ; 7BEA43BA  
 76 pop         esi                               ; 7BEA43BC  
 77 pop         edi                               ; 7BEA43BD  
 78 pop         ebx                               ; 7BEA43BE  
 79 xor         eax,eax                           ; 7BEA43BF  
 80 ret                                           ; 7BEA43C1  
 81 lea         eax,[edx-1]                       ; 7BEA43C2  
 82 pop         ebx                               ; 7BEA43C5  
 83 ret                                           ; 7BEA43C6  
 84 mov         eax,dword ptr [edx-4]             ; 7BEA43C7  
 85 cmp         al,bl                             ; 7BEA43CA  
 86 je          7BEA4404                          ; 7BEA43CC  
 87 test        al,al                             ; 7BEA43CE  
 88 je          7BEA43BC                          ; 7BEA43D0  
 89 cmp         ah,bl                             ; 7BEA43D2  
 90 je          7BEA43FD                          ; 7BEA43D4  
 91 test        ah,ah                             ; 7BEA43D6  
 92 je          7BEA43BC                          ; 7BEA43D8  
 93 shr         eax,10h                           ; 7BEA43DA  
 94 cmp         al,bl                             ; 7BEA43DD  
 95 je          7BEA43F6                          ; 7BEA43DF  
 96 test        al,al                             ; 7BEA43E1  
 97 je          7BEA43BC                          ; 7BEA43E3  
 98 cmp         ah,bl                             ; 7BEA43E5  
 99 je          7BEA43EF                          ; 7BEA43E7  
100 test        ah,ah                             ; 7BEA43E9  
101 je          7BEA43BC                          ; 7BEA43EB  
102 jmp         7BEA4380                          ; 7BEA43ED  
103 pop         esi                               ; 7BEA43EF  
104 pop         edi                               ; 7BEA43F0  
105 lea         eax,[edx-1]                       ; 7BEA43F1  
106 pop         ebx                               ; 7BEA43F4  
107 ret                                           ; 7BEA43F5  
108 lea         eax,[edx-2]                       ; 7BEA43F6  
109 pop         esi                               ; 7BEA43F9  
110 pop         edi                               ; 7BEA43FA  
111 pop         ebx                               ; 7BEA43FB  
112 ret                                           ; 7BEA43FC  
113 lea         eax,[edx-3]                       ; 7BEA43FD  
114 pop         esi                               ; 7BEA4400  
115 pop         edi                               ; 7BEA4401  
116 pop         ebx                               ; 7BEA4402  
117 ret                                           ; 7BEA4403  
118 lea         eax,[edx-4]                       ; 7BEA4404  
119 pop         esi                               ; 7BEA4407  
120 pop         edi                               ; 7BEA4408  
121 pop         ebx                               ; 7BEA4409  
122 ret                                           ; 7BEA440A 
View Code

 

由于这里是反汇编,是加载到内存中的,没有标号,跳转的时候指定的是跳转目标地址,这里将地址放到语句之后。

这里可以看到,反汇编代码和我们之前看到的明显不同,应该就是之前包含进来的代码了。那么,它都做了些什么呢?

在这里,我们从intel文档中查看几个命令:

 

pshuflw:

 

 movlhps:

 

 movdqu:

 

 

pxor:

 

 pcmpeqb:

 

 pmovmskb:

 

 cmovne:

 

 bsf:

 

 第一行和第二行,是一个判断,我们看到,如果 ds:[7BEB92DCh] 处的值大于或等于1,则跳转到第 34 行(7BEA4348),为什么会有这个跳转呢?我们看看34行代码,并和之前看到的汇编代码作比较:

 

我们发现,后面的代码和之前我们看到的汇编源代码一模一样。所以,这里我们可以认为是在判断是否支持SSE,至于为什么如此判断,暂时还没有调查清楚,如果有清楚的,麻烦在评论区回复下,不胜感激。

在了解到执行代码后面部分一致的情况下,我们此时就可以将重点放在前半部分了,为了方便,我们这里再次引用反汇编得到的代码前面部分,并忽略第一句,如下:

 

 1 movzx       eax,byte ptr [esp+8]              ; 7BEA42E9  
 2 mov         edx,eax                           ; 7BEA42EE  
 3 shl         eax,8                             ; 7BEA42F0  
 4 or          edx,eax                           ; 7BEA42F3  
 5 movd        xmm3,edx                          ; 7BEA42F5  
 6 pshuflw     xmm3,xmm3,0                       ; 7BEA42F9  
 7 movlhps     xmm3,xmm3                         ; 7BEA42FE  
 8 mov         edx,dword ptr [esp+4]             ; 7BEA4301  
 9 mov         ecx,0Fh                           ; 7BEA4305  
10 or          eax,0FFFFFFFFh                    ; 7BEA430A  
11 and         ecx,edx                           ; 7BEA430D  
12 shl         eax,cl                            ; 7BEA430F  
13 sub         edx,ecx                           ; 7BEA4311  
14 movdqu      xmm1,xmmword ptr [edx]            ; 7BEA4313  
15 pxor        xmm2,xmm2                         ; 7BEA4317  
16 pcmpeqb     xmm2,xmm1                         ; 7BEA431B  
17 pcmpeqb     xmm1,xmm3                         ; 7BEA431F  
18 por         xmm2,xmm1                         ; 7BEA4323  
19 pmovmskb    ecx,xmm2                          ; 7BEA4327  
20 and         ecx,eax                           ; 7BEA432B  
21 jne         7BEA4337                          ; 7BEA432D  
22 or          eax,0FFFFFFFFh                    ; 7BEA432F  
23 add         edx,10h                           ; 7BEA4332  
24 jmp         7BEA4313                          ; 7BEA4335  
25 bsf         eax,ecx                           ; 7BEA4337  
26 add         eax,edx                           ; 7BEA433A  
27 movd        edx,xmm3                          ; 7BEA433C  
28 xor         ecx,ecx                           ; 7BEA4340  
29 cmp         dl,byte ptr [eax]                 ; 7BEA4342  
30 cmovne      eax,ecx                           ; 7BEA4344  
31 ret                                           ; 7BEA4347

 

 

1 - 4行:原理和之前类似,[esp+8] 处为要查找的目标字符值,到第四行,将edx寄存器的0-7位,和8-15位均设置为查找目标字符。

5 - 7行:将 xmm3 寄存器的每字节均设置为目标字符值,如果我们查找 'x' 字符,则 xmm3 的值为 "xxxxxxxxxxxxxxxx"。

8 - 13行:将查询的源字符串的地址进行16位对齐,并指向源字符串地址之前。并将 eax 值左移目标字符串地址的低四位值(意味着将eax作为之后取值的mask);

14 - 31 行:是查询的主要逻辑,下面我们详细讨论。

第14行,将edx指针指向内存开始的16字节复制到 xmm1寄存器中;

第15行,将xmm2寄存器所有字节设置为0;

第16行,将xmm1中所有的字节与xmm2字节做比较,比较结果放到xmm2中(如果xmm1中字节和xmm2中字节相等,则xmm2对应字节将被设置为0xff,否则将被设置为0x00);

第17行,将xmm3(每个字节值均为目标字符值)和xmm1中的字节做比较,结果放置到xmm1中,如果是第一次做比较,则xmm1中可能存有查找字符串之前的内容,而且有可能包含查找的目标字符,这种情况将在第20行进行处理;

第18行,将xmm1中(源字符串中16个字符或者字符串n个字节内容+字符串前n个字符和要查找的目标字符的比对结果,即前n个字符的擦护照结果)和xmm2中内容执行字节或运算。并将结果放到 xmm2 中。

第19行,按照xmm2中字节内容,生成一个 mask值,并将其放到 ecx 寄存器;

第20行,将ecx和eax(如果第一次,有效位被第12行设置为1,无效位被置为0)执行 and 运算;

第21行,如果20行执行结果不为0(说明找到了目标值),跳转到第25行;

第22行,eax 所有位均置为1;

第23行,将源字符串指针前移16(10h)个字节;

第24行,跳转到循环初始位置,开始新的一轮循环;

第25行,查找ecx中为1的位的编号,并将编号放置到eax;

第26行,将源字符串指针前移eax个字节,这就是目标字符所在位置了;

第27行,从xmm3(每个字节均为查找目标字符)中读取4个字节,并将值放置到edx中;

第28行,置ecx为0,以备返回(目标字符未找到);

第29行,比较dl(edx最低字节)和eax指向的字符;

第30行,如果29行比较结果不相等,说明没有找到目标字符,就置eax为0,否则eax不变;

第31行,返回;

 

这里可以看到,因为使用了 xmm 寄存器,我们每次都处理了16个字节,那为什么效率提升不是16倍呢?只能归结为xmm寄存器增加了电路复杂性,使得处理周期增加了一倍吧。

 

posted @ 2021-04-04 17:33  plle  阅读(239)  评论(0编辑  收藏  举报