簡體   English   中英

訪問文件中的單個字符效率低下? (C ++)

[英]Accessing individual characters in a file inefficient? (C++)

在處理文本文件時,我總是認為它更有效率,首先將內容(或部分內容)讀入std :: string或char數組,因為 - 從我有限的理解 - 文件從內存中讀取塊大於單個字符的大小。 但是,我聽說現代操作系統通常不會直接從文件中讀取,這使得我手動緩沖輸入的好處不大。

假設我想確定文本文件中某個字符的編號。 以下是否會效率低下?

while (fin.get(ch)) {
    if (ch == 'n')
        ++char_count;
}

當然,我想這取決於文件大小,但是有沒有人對什么是最好的方法有任何一般規則?

這里很大程度上取決於您/您的應用程序的確切性能。 反過來,這往往取決於你正在處理的文件有多大 - 如果你正在處理幾十或幾百千字節的事情,你通常應該編寫最簡單的代碼,而不用擔心它 - 你能做的任何事情都將是基本的瞬間,因此優化代碼並不會真正實現。

另一方面,如果您正在處理大量數據 - 大約數十兆字節或更多,那么效率差異就會變得相當大。 除非你采取相當具體的措施來繞過它(如使用read )所有的讀取將要緩存-但是,這並不意味着他們都會是相同的速度(或必然甚至非常接近相同的速度)。

例如,讓我們嘗試快速測試幾種不同的方法來完成你所詢問的內容:

#include <stdio.h>
#include <iomanip>
#include <iostream>
#include <iterator>
#include <fstream>
#include <time.h>
#include <string>
#include <algorithm>

unsigned count1(FILE *infile, char c) { 
    int ch;
    unsigned count = 0;

    while (EOF != (ch=getc(infile)))
        if (ch == c)
            ++count;
    return count;
}

unsigned int count2(FILE *infile, char c) { 
    static char buffer[4096];
    int size;
    unsigned int count = 0;

    while (0 < (size = fread(buffer, 1, sizeof(buffer), infile)))
        for (int i=0; i<size; i++)
            if (buffer[i] == c)
                ++count;
    return count;
}

unsigned count3(std::istream &infile, char c) {    
    return std::count(std::istreambuf_iterator<char>(infile), 
                    std::istreambuf_iterator<char>(), c);
}

unsigned count4(std::istream &infile, char c) {    
    return std::count(std::istream_iterator<char>(infile), 
                    std::istream_iterator<char>(), c);
}

template <class F, class T>
void timer(F f, T &t, std::string const &title) { 
    unsigned count;
    clock_t start = clock();
    count = f(t, 'N');
    clock_t stop = clock();
    std::cout << std::left << std::setw(30) << title << "\tCount: " << count;
    std::cout << "\tTime: " << double(stop-start)/CLOCKS_PER_SEC << "\n";
}

int main() {
    char const *name = "test input.txt";

    FILE *infile=fopen(name, "r");

    timer(count1, infile, "ignore");

    rewind(infile);
    timer(count1, infile, "using getc");

    rewind(infile);
    timer(count2, infile, "using fread");

    fclose(infile);

    std::ifstream in2(name);
    in2.sync_with_stdio(false);
    timer(count3, in2, "ignore");

    in2.clear();
    in2.seekg(0);
    timer(count3, in2, "using streambuf iterators");

    in2.clear();
    in2.seekg(0);
    timer(count4, in2, "using stream iterators");

    return 0;
}

我用一個大約44兆字節的文件作為輸入運行它。 使用VC ++ 2012編譯時,我得到以下結果:

ignore                          Count: 400000   Time: 2.08
using getc                      Count: 400000   Time: 2.034
using fread                     Count: 400000   Time: 0.257
ignore                          Count: 400000   Time: 0.607
using streambuf iterators       Count: 400000   Time: 0.608
using stream iterators          Count: 400000   Time: 5.136

使用相同的輸入,但使用g ++ 4.7.1編譯:

ignore                          Count: 400000   Time: 0.359
using getc                      Count: 400000   Time: 0.339
using fread                     Count: 400000   Time: 0.243
ignore                          Count: 400000   Time: 0.697
using streambuf iterators       Count: 400000   Time: 0.694
using stream iterators          Count: 400000   Time: 1.612

因此,即使所有讀取都被緩沖,我們看到g ++的變化約為8:1,VC ++的變化約為20:1。 當然,我沒有測試(甚至接近)讀取輸入的每種可能方式。 如果我們測試更多的閱讀技術,我懷疑我們會看到更廣泛的時間,但我可能錯了。 無論我們是否這樣做,我們都會看到足夠的變化,至少如果您處理大量數據,那么選擇一種技術而不是另一種技術來提高處理速度是合理的。

不,你的代碼是有效的。 文件旨在按順序讀取。 在幕后,保留一塊RAM以緩沖輸入的數據流。 事實上,因為您在讀取整個文件之前開始處理數據,所以while循環應該稍早完成。 此外,您可以毫不費力地處理遠遠超過計算機主RAM的文件。

編輯:令我驚訝的是,傑里的號碼已經消失了。 我原以為通過讀取和解析塊而獲得的任何效率都會因讀取文件的成本而相形見絀。 我真的很想知道這個時間花在哪里以及文件未緩存時變化的程度。 不過,我必須推薦Jerry對這個問題的回答,特別是他指出,在你知道自己遇到性能問題之前,你真的不應該擔心它。

它在很大程度上取決於上下文,並且由於沒有圍繞代碼的上下文,因此很難說。

毫無疑問,你的操作系統可能正在為你緩存至少一小部分文件,正如其他人所說的那樣......然而,在用戶和內核之間來回轉換是很昂貴的,而這可能就是你的瓶頸所在。

如果你要插入fin.rdbuf()->pubsetbuf(NULL, 65536); 在此代碼之前,您可能會注意到顯着的加速。 這是標准庫嘗試一次從內核讀取65536個字節的提示,並保存它們供以后使用,而不是在每個字符的用戶和內核之間來回傳遞。

暫無
暫無

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

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