仍是上次那個json解析程序的優化, 雖然速度已經比較理想, 可是看到IndexOf佔到整個解析時長的20%+, 內心仍是不爽. 我寫的IndexOf是按照UTF16字符, 一個字符一個字符去比較的, 因此當時能想到的辦法, 就是一次比較多個字符.算法
大概是五六年前, 我看過某一個libc裏面實現的strlen, 算法就是((i - 0x1) & ~i) & 0x80
, 這個算法只能針對\0
比較, 因此我在想, 若是我把i
矯正成0, 那麼算法應該就會變得通用. 而後擼了一下午, 寫了一個一次比較4個UTF16 char的版本.json
1 const ulong l1u = 0x1ul | 0x1ul << 16 | 0x1ul << 32 | 0x1ul << 48; 2 const ulong l80u = 0x8000ul | 0x8000ul << 16 | 0x8000ul << 32 | 0x8000ul << 48; 3 4 public static int IndexOf(char* s, int length, char c) 5 { 6 int offset = 0; 7 int align = (int)s & 0x7; 8 //8 byte aligned or 4 char aligned 9 if (align != 0) 10 { 11 int index = IndexOf(s, offset, align >> 1, c); 12 if (index >= 0) return index; 13 } 14 //strlen's quick check is based on ((i - 0x1) & ~i) & 0x80 15 //so, if i add `-expectedChar` onto i 16 //then i can reuse this formula 17 ulong magic = (ulong)c; 18 ulong expected = unchecked(magic | magic << 16 | magic << 32 | magic << 48); 19 offset += align >> 1; 20 int padLength = (length - offset) >> 2 << 2; 21 for (; offset < padLength; offset += 4) 22 { 23 ulong p = *(ulong*)(s + offset); 24 p = p >= expected ? unchecked(p - expected) : unchecked(expected - p); 25 if (unchecked((p - l1u) & ~p & l80u) != 0) 26 { 27 return IndexOf(s, offset, 4, c); 28 } 29 } 30 return IndexOf(s, offset, length - offset, c); 31 } 32 33 private static int IndexOf(char* s, int offset, int length, char c) 34 { 35 if (length > 4) throw new System.Exception("FUCK U"); 36 char* p = s + offset; 37 switch (length) 38 { 39 case 4: goto L4; 40 case 3: goto L3; 41 case 2: goto L2; 42 case 1: goto L1; 43 } 44 L4: if (*p++ == c) goto Found; 45 L3: if (*p++ == c) goto Found; 46 L2: if (*p++ == c) goto Found; 47 L1: if (*p++ == c) goto Found; 48 return -1; 49 Found: return (int)(p - s); 50 }
費勁了腦汁, 結果他媽的, 跑的還沒一次比較四個char的快(就是四次if)…..優化
媽蛋, 我得何時去研究一下這個代碼爲啥會慢, 感受不科學啊ui
感受這就是玄學this
PS:spa
今天把代碼改了一下, 也沒改多少, 就是之前一次掃描一個ulong
, 如今一次掃描兩個ulong
, 算法仍是同樣的.code
在x64下面, 速度是要比一次比較4個char來的快, 快50%以上; 不過在x86下面, 速度比4個char的版本慢一點, 應該是x86面, int64是模擬出來的緣故.orm