[英]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步跳
__mm_cmpestri
索引
所有這16個字節中的定界符和\\0
終止符 0
填充其他 0
,再次使用SSE指令通過一條指令( _mm_sub_epi8
)最多進行16次減法 _mm_cvtepi8_epi64
,我認為) [10^15 10^14]
初始化__mm128
寄存器,我們稱它為powers
powers
[100 100]
powers
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.