Модераторы: feodorv, GremlinProg, xvr, Fixin

Поиск:

Ответ в темуСоздание новой темы Создание опроса
> Быстрое чтение файлов больших размеров, Сравнение различных вариантов 
V
    Опции темы
FiMa1
Дата 24.10.2007, 07:47 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Опытный
**


Профиль
Группа: Участник
Сообщений: 408
Регистрация: 23.9.2006

Репутация: нет
Всего: 6



Доброго времени суток, друзья!

Мне было необходимо прочитать файл, размером более или приближенном к 1 Мб. Выбранное решение не показалось мне достаточно быстрым. Поиски предложений на эту тему в Интернете не принесли много результатов.
Однако я выяснил, что быстрее всего будет либо прочитать файл одним куском, читать его кусками по 2 кб, или читать, предварительно открыв файл посредством функции CreateFile (Platform SDK: File Systems). Выгодным, кроме того, является чтение файла предварительно открытого в бинарном (untranslated) режиме.
Я решил сравнить различные варианты, использованный мной код и результаты приведены ниже.
Использованный код:
Код
#include <iostream>
#include <fstream>
#include <Windows.h>
using namespace std;

#define TWO_KILOBYTES 2048

long getfsize(FILE *stream)
{
    // store current cursor position
    long curpos = ftell(stream);
    // set cursor to the end of the file
    fseek(stream, 0L, SEEK_END);
    // get cursor position
    long length = ftell(stream);
    // back cursor to the previous pos
    fseek(stream, curpos, SEEK_SET);

    return length;
}

void CReadCharByChar(FILE *fp, char *text, int bytes)
{
    char ch;

    while( (ch = getc(fp)) != EOF && --bytes >= 0)
        *text++ = ch;
}

void CPPReadCharByChar1(ifstream &fin, char *dest, int bytenum)
{
    --bytenum; // '-1' 'cause we'll fill dest from its end
    do {
        fin >> *dest++;
    } while(--bytenum >= 0);
}

void CPPReadCharByChar2(ifstream &fin, char *text)
{
    while( ! fin.get(*text++).fail());
}

void CPPReadAtATime(ifstream &fin, char *text, int bytes)
{
    fin.read(text, bytes);
}

void СReadByTwoKbs(FILE *fp, char *text)
{
    while( ! feof(fp) )
    {
        fgets(text, TWO_KILOBYTES, fp);
        text += 2047; // TWO_KILOBYTES - '\0' which is added by fgets
    }
}

void HANDLEReading(HANDLE hFile, // handle to the file to be read
                   char*  text,  // pointer to the buffer that receives the data
                   int    bytes) // number of bytes to be read
{
    DWORD *nbytes = new DWORD;
    ReadFile(hFile,  // handle to the file to be read
             text,   // pointer to the buffer that receives the data
             bytes,  // number of bytes to be read
             nbytes, // pointer to the variable that receives the number of bytes read
             NULL);
}

int main()
{
    // open file in TEXT mode to read (C style of opening)
    FILE *ctxtfin;
    if( fopen_s(&ctxtfin, "c:\\eng.dic", "r") != 0 )
    {
        std::cout << "Error: can't open input file\n";
        exit(1);
    }

    // open file in TEXT mode to read (C++ style of opening)
    ifstream cpptxtfin("c:\\eng.dic", ios_base::in);
    if( cpptxtfin.fail() )
    {
        std::cout << "Error: can't open input file\n";
        exit(1);
    }

    // get file size
    long fsize = getfsize(ctxtfin);
    const long BUFSIZE = fsize + 1; // +'\0'
    // allocate memory for text buffer
    char *text = new char[BUFSIZE]; 

    CReadCharByChar(ctxtfin, text, fsize);
    text[BUFSIZE - 1] = '\0';

    CPPReadCharByChar1(cpptxtfin, text, fsize);
    text[BUFSIZE - 1] = '\0';

    CPPReadCharByChar2(cpptxtfin, text);
    text[BUFSIZE - 1] = '\0';

    fread( text, sizeof( char ), fsize, ctxtfin );
    text[BUFSIZE - 1] = '\0';

    fclose(ctxtfin);
    cpptxtfin.close();

    // open file in BINARY mode to read (C style of opening)
    FILE *cbinfin;
    if( fopen_s(&cbinfin, "c:\\eng.dic", "rb") != 0 )
    {
        std::cout << "Error: can't open input file\n";
        exit(1);
    }

    // open file in BINARY mode to read (C++ style of opening)
    ifstream cppbinfin("c:\\eng.dic", ios::in | ios::binary);
    if( cppbinfin.fail() )
    {
        std::cout << "Error: can't open input file\n";
        exit(1);
    }

    СReadByTwoKbs(cbinfin, text);

    CPPReadAtATime(cppbinfin, text, fsize);
    text[BUFSIZE - 1] = '\0';

    fclose(cbinfin);
    cppbinfin.close();

    HANDLE hFile = CreateFile(TEXT("c:\\eng.dic"),   // file to open
                              FILE_READ_DATA,        // open for reading
                              FILE_SHARE_READ,       // share for reading
                              NULL,                  // default security
                              OPEN_EXISTING,         // existing file only
                              FILE_ATTRIBUTE_NORMAL, // normal file
                              NULL);
    
    if(hFile == INVALID_HANDLE_VALUE)
    {
        printf("Could not open file (error %d)\n", GetLastError());
        return 0;
    }

    HANDLEReading(hFile, text, fsize);
    text[BUFSIZE - 1] = '\0';

    CloseHandle(hFile);
    delete[] text;

    return 0;
}

Результаты (для текстового файла размером 689674 байтов):
Код
FUNCTION              | OPENING MODE  | TIME OF WORK, CPU TICS
-------------------------------------------------------------------------
fread                  text                   11761192,8     
                       binary                  2565651,6          
---------------------------------------------------------------------------
CReadCharByChar        text                  748919799,0
                       binary                739766095,2
---------------------------------------------------------------------------
CPPReadCharByChar1     text                 1307545578,0
                       binary               1173414586,0
---------------------------------------------------------------------------
CPPReadCharByChar2     text                  933057450,0
                       binary                924524164,8
---------------------------------------------------------------------------
CPPReadAtATime         text                   12956679,0
                       binary                  3689483,4
---------------------------------------------------------------------------
СReadByTwoKbs          text                   24776996,4
                       binary                 15561489,6
---------------------------------------------------------------------------
HANDLEReading          -                       2333055,6
---------------------------------------------------------------------------

Таким образом, в моем случае, наиболее быстрым оказался вариант с использованием ReadFile (Platform SDK: File Systems) и fread (<stdio.h>), с предварительным открытием файла в бинарном режиме. Самым медленным оказался вариант с посимвольным чтением в стиле C++:
Код
void CPPReadCharByChar1(ifstream &fin, char *dest, int bytenum)
{
    --bytenum; // '-1' 'cause we'll fill dest from its end
    do {
        fin >> *dest++;
    } while(--bytenum >= 0);
}

Если у вас есть какие-либо другие предложения, огласите, пожалуйста.
PM   Вверх
SergeCpp
Дата 24.10.2007, 08:19 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


 
**


Профиль
Группа: Участник
Сообщений: 955
Регистрация: 8.8.2005
Где: At Home

Репутация: 10
Всего: 124



Попробуйте вот так вот

Код
DWORD crc_file( PSTR pFile )
{
    DWORD crc( 0 );

    HANDLE hFile( CreateFile(
        pFile,
        GENERIC_READ,
        FILE_SHARE_READ,
        NULL,
        OPEN_EXISTING,
        FILE_FLAG_SEQUENTIAL_SCAN,
        NULL ) );

    if( hFile != INVALID_HANDLE_VALUE )
    {
        HANDLE hMap( CreateFileMapping(
            hFile,
            NULL,
            PAGE_READONLY,
            0x00000000,
            0x00000000,
            NULL ) );

        DWORD FileLen( GetFileSize( hFile, NULL ) );

        CloseHandle( hFile );

        if( hMap )
        {
            // FileLen > 0

            BYTE( *pView )( ( BYTE* )( MapViewOfFile(
                hMap,
                FILE_MAP_READ,
                0x00000000,
                0x00000000,
                0 ) ) );

            CloseHandle( hMap );

            if( pView )
            {
                crc = crc_mem  ( pView, FileLen );
                UnmapViewOfFile( pView );
            }
        }
    }

    return crc;
}


Это пример из программы вычисления CRC32, где весь файл должен быть прочтён последовательно

PM MAIL WWW ICQ   Вверх
FiMa1
Дата 24.10.2007, 09:17 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Опытный
**


Профиль
Группа: Участник
Сообщений: 408
Регистрация: 23.9.2006

Репутация: нет
Всего: 6



Цитата(SergeCpp @ 24.10.2007,  08:19)
Попробуйте вот так вот

Код
DWORD crc_file( PSTR pFile )
{
    DWORD crc( 0 );

    HANDLE hFile( CreateFile(
        pFile,
        GENERIC_READ,
        FILE_SHARE_READ,
        NULL,
        OPEN_EXISTING,
        FILE_FLAG_SEQUENTIAL_SCAN,
        NULL ) );

    if( hFile != INVALID_HANDLE_VALUE )
    {
        HANDLE hMap( CreateFileMapping(
            hFile,
            NULL,
            PAGE_READONLY,
            0x00000000,
            0x00000000,
            NULL ) );

        DWORD FileLen( GetFileSize( hFile, NULL ) );

        CloseHandle( hFile );

        if( hMap )
        {
            // FileLen > 0

            BYTE( *pView )( ( BYTE* )( MapViewOfFile(
                hMap,
                FILE_MAP_READ,
                0x00000000,
                0x00000000,
                0 ) ) );

            CloseHandle( hMap );

            if( pView )
            {
                crc = crc_mem  ( pView, FileLen );
                UnmapViewOfFile( pView );
            }
        }
    }

    return crc;
}


Это пример из программы вычисления CRC32, где весь файл должен быть прочтён последовательно

Среднее пяти замеров: 481824 тиков ЦПУ (Solution Configuretion = Release) smile  СПАСИБО!!!
PM   Вверх
DubrovinAV
Дата 27.10.2007, 10:47 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Шустрый
*


Профиль
Группа: Участник
Сообщений: 54
Регистрация: 20.9.2007

Репутация: нет
Всего: нет



Откройте тайну, как показания системного таймера снимали? Интересует консоль. 
Заранее спасибо. 
PM MAIL   Вверх
Alexeis
Дата 27.10.2007, 10:52 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Амеба
Group Icon


Профиль
Группа: Админ
Сообщений: 11743
Регистрация: 12.10.2005
Где: Зеленоград

Репутация: 7
Всего: 459



GetTickCount() 



--------------------
Vit вечная память.

Обсуждение действий администрации форума производятся только в этом форуме

гениальность идеи состоит в том, что ее невозможно придумать
PM ICQ Skype   Вверх
SergeCpp
Дата 27.10.2007, 15:42 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


 
**


Профиль
Группа: Участник
Сообщений: 955
Регистрация: 8.8.2005
Где: At Home

Репутация: 10
Всего: 124



Концептуально правильным является вот такое вот измерение

Код
HANDLE h( GetCurrentProcess() );

FILETIME Creation;
FILETIME Exit;

FILETIME KernelBeg;
FILETIME UserBeg;

GetProcessTimes( h, &Creation, &Exit, &KernelBeg, &UserBeg );

// работаем...

FILETIME KernelEnd;
FILETIME UserEnd;

GetProcessTimes( h, &Creation, &Exit, &KernelEnd, &UserEnd );

// вычисляем разность...


При необходимости (многопоточная программа) меняем Process на Thread

Вот тут вот есть детали

PM MAIL WWW ICQ   Вверх
Alexeis
Дата 27.10.2007, 15:57 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Амеба
Group Icon


Профиль
Группа: Админ
Сообщений: 11743
Регистрация: 12.10.2005
Где: Зеленоград

Репутация: 7
Всего: 459



Здорово. Выходит что эта функция измеряет чистое время работы фрагмента кода. Мне раньше приходилось делать несколько испытаний чтобы замерить минимальное время!
  А этот алгоритм считает время которое тратит контроллер DMA на запись данных в ОЗУ? Ведь передача происходит без участия процессора.


--------------------
Vit вечная память.

Обсуждение действий администрации форума производятся только в этом форуме

гениальность идеи состоит в том, что ее невозможно придумать
PM ICQ Skype   Вверх
SergeCpp
Дата 27.10.2007, 16:15 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


 
**


Профиль
Группа: Участник
Сообщений: 955
Регистрация: 8.8.2005
Где: At Home

Репутация: 10
Всего: 124



Вот тут вот кое-какие комментарии почитайте

Вот эту вот ссылку, к примеру

(по Vista Kernel, вроде бы три статьи Руссиновича, я их тут давал где-то в Разных вопросах, но давно, так что поищите там у Микрософта, статьи весьма и весьма)

PM MAIL WWW ICQ   Вверх
DubrovinAV
Дата 27.10.2007, 19:04 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Шустрый
*


Профиль
Группа: Участник
Сообщений: 54
Регистрация: 20.9.2007

Репутация: нет
Всего: нет



Спасибо, работает.

А время он измеряет в интервалах, равных 100нс?



Это сообщение отредактировал(а) DubrovinAV - 27.10.2007, 19:35
PM MAIL   Вверх
SergeCpp
Дата 27.10.2007, 19:52 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


 
**


Профиль
Группа: Участник
Сообщений: 955
Регистрация: 8.8.2005
Где: At Home

Репутация: 10
Всего: 124



Почитайте вот эту вот статью

Вот так вот можно
Код
union F64 { FILETIME f; __int64 i; F64( FILETIME f ) : f( f ) {} operator __int64() { return i; } };

F64 kb( KernelBeg );
F64 ke( KernelEnd );

__int64 k( ke - kb );



Это сообщение отредактировал(а) SergeCpp - 29.10.2007, 12:59
PM MAIL WWW ICQ   Вверх
Alexeis
Дата 27.10.2007, 20:14 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Амеба
Group Icon


Профиль
Группа: Админ
Сообщений: 11743
Регистрация: 12.10.2005
Где: Зеленоград

Репутация: 7
Всего: 459



Цитата(DubrovinAV @  27.10.2007,  19:04 Найти цитируемый пост)
А время он измеряет в интервалах, равных 100нс?

  Похоже что с шагом в 15.625 мс.


--------------------
Vit вечная память.

Обсуждение действий администрации форума производятся только в этом форуме

гениальность идеи состоит в том, что ее невозможно придумать
PM ICQ Skype   Вверх
MAKCim
Дата 28.10.2007, 10:07 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Воін дZэна
****


Профиль
Группа: Экс. модератор
Сообщений: 5644
Регистрация: 10.12.2005
Где: Менск, РБ

Репутация: 1
Всего: 207



Цитата

По умолчанию клиентские версии ОС Windows позволяют выполнение потоков длительностью до 2 тактов (до 6 тактов на переднем плане). Однако на самом деле поток может совсем не получить времени ЦП, или же получить до 6 циклов (до 18 циклов на переднем плане), в зависимости от его поведения и прочей активности в системе.

какие такты имеются в виду?
внутренние такты логического процессора?
извините, но это бред
нет такого точного таймера
тот же HPET работает на частоте 69.841279 нс
возьмем, к примеру, какой-нибудь C2D с System Bus Frequency (SBF) = 266 Mhz
и Internal Frequency Rate (IFR) = 7
Internal Clock Frequency =  SBF * IFR = 1862 Mhz
т. е чтобы отследить 2 такта нужен таймер работающий как минимум на частоте 1.862 нс
ни HPET, ни APIC таймеры не позволяют отсчитывать промежутки времени с такой точностью



--------------------
Ах, у елі, ах, у ёлкі, ах, у елі злыя волкі ©

PM MAIL   Вверх
W4FhLF
Дата 28.10.2007, 10:54 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


found myself
****


Профиль
Группа: Участник Клуба
Сообщений: 2831
Регистрация: 2.12.2006

Репутация: 12
Всего: 121



MAKCim, может внутренний счётчик процессора, который считывается по rdtsc? Но, учитывая сложность современных процессоров, там тоже мерятся не совсем такты.  


--------------------
"Бог умер" © Ницше
"Ницше умер" © Бог
PM ICQ   Вверх
SergeCpp
Дата 28.10.2007, 12:00 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


 
**


Профиль
Группа: Участник
Сообщений: 955
Регистрация: 8.8.2005
Где: At Home

Репутация: 10
Всего: 124



Это перевод такой неясный

Речь идёт об интервалах планировщика задач

У Руссиновича (Внутреннее устройство...) и у Рихтера об этом говорится

И вот тут говорится тоже

PM MAIL WWW ICQ   Вверх
SergeCpp
Дата 28.10.2007, 12:19 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


 
**


Профиль
Группа: Участник
Сообщений: 955
Регистрация: 8.8.2005
Где: At Home

Репутация: 10
Всего: 124



Цитата(W4FhLF @  28.10.2007,  12:54 Найти цитируемый пост)
Но, учитывая сложность современных процессоров, там тоже меряются не совсем такты

Меряются-то там такты, но вот использование намеренного там для определения времени (частота процессора меняться может) или числа тактов выполнения чего-либо (конвейер etc) — уже затруднено весьма

PM MAIL WWW ICQ   Вверх
Ответ в темуСоздание новой темы Создание опроса
Правила форума "C/C++: Системное программирование и WinAPI"
Fixin
GremlinProg
xvr
feodorv
  • Большое количество информации и примеров с использованием функций WinAPI можно найти в MSDN
  • Описание сообщений, уведомлений и примеров с использованием компонент WinAPI (BUTTON, EDIT, STATIC, и т.п.), можно найти в MSDN Control Library
  • Непосредственно, перед созданием новой темы, проверьте заголовок и удостоверьтесь, что он отражает суть обсуждения.
  • После заполнения поля "Название темы", обратите внимание на наличие и содержание панели "А здесь смотрели?", возможно Ваш вопрос уже был решен.
  • Приводите часть кода, в которой предположительно находится проблема или ошибка.
  • Если указываете код, пользуйтесь тегами [code][/code], или их кнопочными аналогами.
  • Если вопрос решен, воспользуйтесь соответствующей ссылкой, расположенной напротив названия темы.
  • Один топик - один вопрос!
  • Перед тем как создать тему - прочтите это .

На данный раздел распространяются Правила форума и Правила раздела С++:Общие вопросы .


Если Вам понравилась атмосфера форума, заходите к нам чаще! С уважением, Chipset, Step, Fixin, GremlinProg, xvr. feodorv.

 
0 Пользователей читают эту тему (0 Гостей и 0 Скрытых Пользователей)
0 Пользователей:
« Предыдущая тема | C/C++: Системное программирование и WinAPI | Следующая тема »


 




[ Время генерации скрипта: 0.0643 ]   [ Использовано запросов: 22 ]   [ GZIP включён ]


Реклама на сайте     Информационное спонсорство

 
По вопросам размещения рекламы пишите на vladimir(sobaka)vingrad.ru
Отказ от ответственности     Powered by Invision Power Board(R) 1.3 © 2003  IPS, Inc.