Re[7]: загрузить файл в std::string не используя буфер
От: _Vladimir_ Россия  
Дата: 04.06.04 06:49
Оценка: 4 (1)
Здравствуйте, ArtDenis, Вы писали:

AD>Здравствуйте, <Аноним>, Вы писали:


А>>Дело не в промежуточном буфере и не в перераспределении памяти,

А>>дело в том что методы "copy" или "insert" производят посимвольное копирование,
А>>вот это и является не оптимальным.
AD>В случае с std::string выбирать не приходиться. Это тебе не std::vector<char>.

А>>посмотри ссылку на которую указал Odi$$ey http://www.rsdn.ru/Forum/Message.aspx?mid=361479.

AD>Там как раз речь идёт о std::vector<char>

Выбор всегда есть, в этом и заключается прелесть программирования!
(Предыдущие сообщения написаны мной, я решил зарегистрироваться)

В сообщении на которое указал Odi$$ey, надо было обратить внимание на время выполнения тестов,
они ясно показывают что использование методов "copy" или "insert" является не оптимальным вариантом.

Я написал свой тест, он показывает что ситуация с string аналогичная vector<char>.

В своем тесте я написал несколько вариантов загрузки файла в строку.
null — пустышка, открывает файл и вычислет его размер (для чистоты эксперимента);
system — использует функции Window для работы с файлом;
system shared buffer — то же что и system, только использует общий буфер;
остальные функции работают с файлом через stl:
buffer — считывает сторку через буфер;
string.resize — использует прямую запись в данные string (опасный вариант!);
string.insert и std::copy — это ваш вариант.

Результаты теста:

test file "D:/temp/test/test1.txt", size 320, count 100000
null — 2023 ms
system — 2053 ms
system shared buffer — 2043 ms
buffer — 5367 ms
string.resize — 4637 ms
string.insert — 5568 ms
std::copy — 5238 ms

test file "D:/temp/test/test2.txt", size 19047, count 10000
null — 200 ms
system — 1031 ms
system shared buffer — 1032 ms
buffer — 3084 ms
string.resize — 3145 ms
string.insert — 10004 ms
std::copy — 8292 ms

test file "D:/temp/test/test3.txt", size 750330, count 1000
null — 20 ms
system — 10595 ms
system shared buffer — 9885 ms
buffer — 15252 ms
string.resize — 14440 ms
string.insert — 40589 ms
std::copy — 33868 ms


Программа собрана на C++Builder 6, с включенной оптимизацией.

Текст программы:

#include <string>
#include <fstream>
#include <algorithm>
#include <streambuf>
#include <iostream>

#include <windows.h>

class t_buffer
{
public:
    void* data;
    int size;

    inline t_buffer( int s = 0 ) { size = s; data = (s > 0) ? malloc(s) : 0; }
    inline ~t_buffer() { size = 0; if (data) { free(data); data = 0; } }

    inline void grow( int s ) { if (size < s) data = realloc(data, size = s); }
};

using namespace std;

int file_size( const string& filename )
{
    ifstream file( filename.c_str(), ios::in | ios::binary );

    file.seekg(0, ios_base::end);
    return file.tellg();
}

//------------------------------------------------------------------------------

string read_str_NULL( const string& filename )
{
    string result;
    ifstream file( filename.c_str(), ios::in | ios::binary );
    size_t filesize;

    file.seekg(0, ios_base::end);
    filesize = file.tellg();
    if (filesize == 0) return result;
    file.seekg(0, ios_base::beg);
    return result;
}

string read_str_SYSTEM( const string& filename )
{
    HANDLE fh;
    string result;
    DWORD filesize;

    fh = ::CreateFile( filename.c_str(), GENERIC_READ, FILE_SHARE_READ, 0, OPEN_EXISTING,
        FILE_ATTRIBUTE_NORMAL | FILE_FLAG_SEQUENTIAL_SCAN, 0 );
    if (fh == INVALID_HANDLE_VALUE) return result;
    filesize = ::GetFileSize(fh, 0);
    if (filesize > 0)
    {
        DWORD readsize = 0;
        t_buffer buffer(filesize);

        if ( ::ReadFile(fh, buffer.data, filesize, &readsize, 0) != 0 )
            result = string((char*)buffer.data, readsize);
    }
    ::CloseHandle(fh);
    return result;
}

string read_str_SYSTEM_SHARED_BUFFER( const string& filename )
{
    static t_buffer buffer;

    HANDLE fh;
    string result;
    DWORD filesize;

    fh = ::CreateFile( filename.c_str(), GENERIC_READ, FILE_SHARE_READ, 0, OPEN_EXISTING,
        FILE_ATTRIBUTE_NORMAL | FILE_FLAG_SEQUENTIAL_SCAN, 0 );
    if (fh == INVALID_HANDLE_VALUE) return result;
    filesize = ::GetFileSize(fh, 0);
    if (filesize > 0)
    {
        DWORD readsize = 0;

        buffer.grow(filesize);
        if ( ::ReadFile(fh, buffer.data, filesize, &readsize, 0) != 0 )
            result = string((char*)buffer.data, readsize);
    }
    ::CloseHandle(fh);
    return result;
}

string read_str_BUFFER( const string& filename )
{
    ifstream file( filename.c_str(), ios::in | ios::binary );
    size_t filesize;

    file.seekg(0, ios_base::end);
    filesize = file.tellg();
    if (filesize == 0) return string();
    file.seekg(0, ios_base::beg);

    t_buffer buffer(filesize);

    file.read((char*)buffer.data, filesize);
    string result((char*)buffer.data, filesize);
    return result;
}

string read_str_RESIZE( const string& filename )
{
    string result;
    ifstream file( filename.c_str(), ios::in | ios::binary );
    size_t filesize;

    file.seekg(0, ios_base::end);
    filesize = file.tellg();
    if (filesize == 0) return result;
    file.seekg(0, ios_base::beg);
    result.resize(filesize);
    file.read( (char*)result.data(), filesize );
    return result;
}

string read_str_INSERT( const string& filename )
{
    string text;
    ifstream file;
    size_t filesize;

    file.open( filename.c_str(), ios::in | ios::binary );

    file.seekg(0, ios_base::end);
    filesize = file.tellg();
    file.seekg(0, ios_base::beg);

    text.reserve(filesize);

    text.insert( text.begin(), istreambuf_iterator<char>(file), istreambuf_iterator<char>() );
    return text;
}

string read_str_COPY( const string& filename )
{
    string text;
    ifstream file;
    size_t filesize;

    file.open( filename.c_str(), ios::in | ios::binary );

    file.seekg(0, ios_base::end);
    filesize = file.tellg();
    file.seekg(0, ios_base::beg);

    text.reserve(filesize);

    copy( istreambuf_iterator<char>(file),
        istreambuf_iterator<char>(), std::back_inserter(text) );
    return text;
}

//------------------------------------------------------------------------------

typedef string (*t_read_method)( const string& );

struct t_method_item
{
    const char* name;
    t_read_method method;
};

static const t_method_item c_methods[] =
{
    { "null", read_str_NULL },
    { "system", read_str_SYSTEM },
    { "system shared buffer", read_str_SYSTEM_SHARED_BUFFER },
    { "buffer", read_str_BUFFER },
    { "string.resize", read_str_RESIZE },
    { "string.insert", read_str_INSERT },
    { "std::copy", read_str_COPY },
    { 0, 0 }
};

void test_methods( int count, const string& filename )
{
    const t_method_item* i;
    int c;
    unsigned int time;

    std::cout << std::endl << "test file \"" << filename << "\", size " << file_size(filename) << ", count " << count << std::endl;
    for ( i = c_methods; i->name; i++ )
    {
        time = ::GetTickCount();
        for ( c = count; c > 0; c-- )
            i->method(filename);
        time = ::GetTickCount() - time;
        std::cout << i->name << " - " << time << " ms" << std::endl;
    }
}

void main()
{
    char t;

    test_methods(100000, "D:/temp/test/test1.txt"); // 320 b
    test_methods( 10000, "D:/temp/test/test2.txt"); // 18,6 Kb
    test_methods(  1000, "D:/temp/test/test3.txt"); // 732 Kb

    std::cin >> t;
}
 
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.