[英]Is there a faster way than fscanf while reading a large data
我正在努力處理大量的海量結構化數據。 我有一個包含名稱和門號的文件。 我正在使用fscanf讀取名稱和那個數字,然后使用fprintf將它們存儲在較小的文件中。
while ( fscanf(file, "%s %d", &people[i].name, &people[i].doorNum) > 0 ) {
...
}
人是一個結構數組
typedef struct {
char* name;
int doorNum;
}person;
我要讀取的文件是15 GB。 我的目標是讀取它們並將其拆分為1GB的文件。 它可以正常工作,但是需要十多分鍾。 如何改善這些讀寫數據過程?
您沒有告訴我們“分裂”的意思。
可能是將字段讀取為一串字符和一個整數是沒有用的(也許一個字符串或兩個單獨的字符串就足夠了)。
使用要匹配的模式的內置知識編寫自己的掃描功能,這肯定會更有效。 即使將自己的轉換寫為整數也應該更好。
fscanf()
具有很多功能,無法同時使用,因此速度較慢。 我建議您使用fread()
編寫自己的函數。 由於您的功能將只有一項特定任務,因此應該更快。
所有文件都包含二進制數據。 有些格式有效,有些則無效。
例如; 要存儲數字0x1234,您可以將其存儲為2個字節的序列0x34, 0x12
以便可以通過少量的簡單/快速操作(例如value = buffer[pos] | (buffer[pos+1] << 8);
)。 這將是相對有效的。
另外; 您可以將其存儲為5個字節的序列0x34, 0x36, 0x36, 0x40, 0x00
其中每個字節代表字符串中的ASCII字符(末尾帶有零終止符); 那么您可以掃描字節,並使用昂貴的循環將其從十進制轉換為整數:
while( (c = buffer[pos++]) != 0) {
if( (c < '0') || (c > '9') ) {
// Error condition(!)
}
value = value * 10 + c - '0';
}
然后,您可以將其包裝在“便利”(例如fscanf()
)中,從而使情況變得更糟,在這種情況下,代碼必須掃描格式字符串以弄清楚它需要執行類似該昂貴的循環的操作。
基本上; 如果您關心性能和/或效率(包括文件大小),則需要停止使用“純文本”並設計適合數據的文件格式; 特別是當您查看15 GB的巨大文件時。
編輯:添加下面的所有內容!
如果您堅持使用“純文本”,則可以通過自己進行更多的解析來獲得更高的性能(例如,使用atoi()
等函數)。 除此之外的下一步是使用您自己的(更專門的)例程,而不是像atoi()
這樣的函數。
除此之外的下一步是使用確定性有限狀態機。 一般想法可能類似於:
switch( state | buffer[pos++] ) {
case START_OF_LINE | 'A':
case START_OF_LINE | 'B':
case START_OF_LINE | 'C':
string_start = pos - 1;
string_length = 1;
state = GETTING_NAME;
break;
case GETTING_NAME | 'A':
case GETTING_NAME | 'B':
case GETTING_NAME | 'C':
string_length++;
break;
case GETTING_NAME | ' ':
number = 0;
state = GETTING_NUMBER;
break;
case GETTING_NUMBER | '0':
number = number * 10;
break;
case GETTING_NUMBER | '1':
number = number * 10 + 1;
break;
case GETTING_NUMBER | '2':
number = number * 10 + 2;
break;
case GETTING_NUMBER | '\n':
create_structure(string, string_length, number);
line++;
state = START_OF_LINE;
break;
default:
// Invalid character
printf("Parse error at line %u!\n", line);
break;
}
希望編譯器將最終使用的巨大switch()
並優化到快速跳轉表中。 當然,用手構造類似的東西很痛苦且容易出錯。 並且您可能可以找到一個“解析器生成器”來為您做這件事(基於規則)。
除此之外,下一步是多線程。 例如,您可以有一個線程在文件中進行掃描以搜索'\\n'
字符,當找到一個字符時,它將把該行交給工作線程(該工作線程可以使用上面的任何方法來解析該字符)。線)。 這樣,您可以讓多個CPU並行解析。
除了所有這些; 您想在解析數據時從磁盤加載數據。 例如; 在處理第一個MiB數據時,要並行加載第二個MiB數據; 並且您不想加載1個MiB,然后解析1個MiB,然后加載下一個MiB,然后解析下一個MiB,依此類推。為此,您需要使用諸如POSIX異步IO函數之類的東西; 或者(在支持預取的64位OS上)使用內存映射文件。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.