簡體   English   中英

如何優化strtok +環礁

[英]How to optimize strtok + atoll

有什么好的方法可以根據執行時間優化此功能? 我的最終目標是解析一個由幾個整數組成的長字符串(每行數千個整數,數千行)。 這是我最初的解決方案。

int64_t get_next_int(char *newLine) {
    char *token=strtok(newLine, " ");
    if( token == NULL ) {
        exit(0);
    }
    return atoll(token);
}

更多詳細信息 :我需要基於strtok的“狀態”實現,因此由strtok實現的填充應該存在於最終字符串中。 環礁不需要任何形式的驗證。

目標系統 :Intel x86_64(至強系列)

相關主題

我寧願使用類似std::istringstream

int64_t get_next_int(std::istringstream& line) {
    int64_t token;
    if(!(line >> token))
         exit(0);
    return token;
}


 std::istringstream line(newLine);

 int64_t i = get_next_int(line);

strtok()具有眾所周知的缺點 ,您根本不想使用它。

首先,我發現大部分時間在信號處理鏈中優化字符串轉換例程都是徒勞的。 您的系統以字符串形式加載數據的速度(這可能會發生在一些大容量存儲中,數據存儲在這里是由無關緊要的,因為它首先不會選擇字符串格式,否則),並且如果將通過PCIe連接的SSD群集以外的所有群集的讀取速度與快速atoll比較,則會發現轉換效率低下,您損失的時間可忽略不計。 如果通過轉換對那個字符串的部分進行流水線加載,則等待轉換所花費的時間甚至不會被轉換遠距離地填滿,因此,即使不進行任何算法優化,流水線/多線程也將消除幾乎所有轉換時間。

我將繼續假設您的包含整數的字符串足夠大。 像是數千萬個整數。 否則,考慮到很少有人抱怨std::iostream性能 ,所有優化可能都還為時過早。

現在,訣竅是一旦轉換例程的性能達到內存帶寬障礙,就無法進行性能優化。 為了最大程度地克服這一障礙,優化CPU緩存的使用至關重要–因此,在此盡可能少地進行線性訪問和改組內存至關重要。 另外,如果您關心速度,則不需要每次都需要轉換幾個數字時都調用函數–調用開銷(保存/恢復堆棧,來回跳轉)非常重要。 因此,如果您追求性能,將立即進行整個字符串的轉換,然后僅訪問結果整數數組。

因此,在具有SSE4.2功能的現代x86處理器上,您大致會遇到類似的情況

外循環,以16步跳

  • 將128位輸入字符串加載到128位SIMD寄存器中
  • 運行類似__mm_cmpestri索引 所有這16個字節中的定界符和\\0終止符
  • 找到的索引的內循環
    • 使用SSE復制/移位/立即指示隔離子字符串; 0填充其他
    • 在上一次迭代之前保存已保存的“最后一個字符”(如果有的話–僅應在每次外循環迭代中進行第一次內循環迭代)
    • 從每個數字中減去0 ,再次使用SSE指令通過一條指令( _mm_sub_epi8 )最多進行16次減法
    • 將八個16位子字轉換為八個128位字,每個字包含兩個打包的64位整數(每16位一個指令, _mm_cvtepi8_epi64 ,我認為)
    • [10^15 10^14]初始化__mm128寄存器,我們稱它為powers
    • 成對循環雙64位字:(每個步驟應為一條SSE指令)
      • 先乘powers
      • 除以[100 100]
      • 與乘第二powers
      • 將結果添加到雙64位累加器
    • 將累加器中的兩個值相加
    • store結果store到整數數組

關於什么

int n= 0;

// Find the token
for ( ; *newline == ' '; newline++)
  ;
if (*newline == 0)
  // Not found
  exit(0);

// Scan and convert the token
for ( ; unsigned(*newline - '0') < 10; newline++)
  n= 10 * n + *newline - '0';
return n;

我首先從您的代碼中獲得的AFA會返回。 似乎在第一次解析時(在空格字符之前),如果它是非數字輸入或字母和數字的組合(開頭是字母),它將恢復為0。 如果在開頭將數字組合起來,它將僅返回數字。 即,您只需要一個字符串即可進行轉換。 因此,您不需要令牌化,只需檢查string是否為null。 您也可以更改返回類型。 因為, 如果您需要具有_exactly_ 64位的類型,請使用(u)int64_t ,如果您需要_至少_ 64位,則(unsigned) long long是完全可以的,就像(u)int_least64_t 我認為您的代碼有點笨拙。 無需簡化即可顯示您真正想要的東西。

/*
 * ascii-to-longlong conversion
 *
 * no error checking; assumes decimal digits
 *
 * efficient conversion: 
 *   start with value = 0
 *   then, starting at first character, repeat the following
 *   until the end of the string:
 *
 *     new value = (10 * (old value)) + decimal value of next character
 *
 */
long long my_atoll(char *instr)
{
  if(str[0] == '\0')
    return -1;

  long long retval;
  int i;

  retval = 0;
  for (; *instr; instr++) {
    retval = 10*retval + (*instr - '0');
  }
  return retval;
}

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM