Форум программистов, компьютерный форум, киберфорум
С++ для начинающих
Войти
Регистрация
Восстановить пароль
Блоги Сообщество Поиск  
 
 
Рейтинг 4.97/35: Рейтинг темы: голосов - 35, средняя оценка - 4.97
 Аватар для bedvit
1210 / 261 / 22
Регистрация: 20.05.2016
Сообщений: 1,147
Записей в блоге: 22

Скорость чтения csv с разных носителей

21.05.2020, 23:27. Показов 9912. Ответов 148
Метки нет (Все метки)

Студворк — интернет-сервис помощи студентам
Добрый день, джентльмены.
На днях набросал простой код поиска нужной строки в CSV-файле, см. под спойлером
Кликните здесь для просмотра всего текста
C++
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
extern "C" __declspec(dllexport) LPXLOPER12  WINAPI FindInCSV(char *  arg1, char *  arg2, wchar_t *  arg3)
{
    LPXLOPER12 OperOut = new XLOPER12;
    OperOut->xltype = xltypeStr | xlbitDLLFree;
    OperOut->val.str = new XCHAR[32767 + 2]; //+1 под размер + 1 под нуль-терминатор
    OperOut->val.str[0] = 0;
    OperOut->val.str[1] = '\0';
    std::filebuf fb;
    if (fb.open(arg1, std::ios::in | std::ios::binary))
    {
        int x = 0;
        int lenStrOut = 0; //0й элемент - размер строки
        int lenArg3 = wcslen(arg3);
        bool overflow = false;
        std::string str;
        std::istream is(&fb);
        XCHAR* strTmp= OperOut->val.str + 1;
 
        while (is) 
        {
            std::getline(is, str);
            size_t found = str.find(arg2);
            if (found != std::string::npos)
            {
                x++;
                if (lenArg3 > 0 && x > 1)
                {
                    if (lenStrOut + lenArg3 > 32767) { overflow = true;  goto end_; }
                    wcscpy(strTmp + lenStrOut, arg3);
                    lenStrOut = lenStrOut + lenArg3;
 
                    if (lenStrOut + str.length() > 32767) { overflow = true;  goto end_; }
                    MultiByteToWideChar(CP_ACP, MB_PRECOMPOSED, str.c_str(), -1, strTmp + lenStrOut, str.length() + 1);
                    lenStrOut = lenStrOut + str.length();
                }
                else if (lenArg3 > 0)
                {
                    if (lenStrOut + str.length() > 32767) { overflow = true;  goto end_; }
                    MultiByteToWideChar(CP_ACP, MB_PRECOMPOSED, str.c_str(), -1, strTmp + lenStrOut, str.length() + 1);
                    lenStrOut = lenStrOut + str.length();
                }
                else
                {
                    if (lenStrOut + str.length() > 32767) { overflow = true;  goto end_; }
                    MultiByteToWideChar(CP_ACP, MB_PRECOMPOSED, str.c_str(), -1, strTmp + lenStrOut, str.length() + 1);
                    lenStrOut = lenStrOut + str.length();
                    goto end_ ;
                }
            };
        }
    end_:
        if (overflow) {
            wcscpy(strTmp, L"Qverflow. Max char 32767\0");
            OperOut->val.str[0] = 24;
        }
        else
        {
            OperOut->val.str[0] = lenStrOut;
        }
        fb.close();
    }
    return OperOut;
}

Сегодня протестировал работу на з-х накопителях:
HDD WDC WD10EACS-00ZJB0 (1000 ГБ, SATA-II)
HDD WDC WD5000AAKX-001CA0 (500 ГБ, 7200 RPM, SATA-III)
SSD KINGSTON SV300S37A120G (120 ГБ, SATA-III)

В итоге время обработки CSW-файла размером 1ГБ у всех примерно одинаковое, что стало для меня откровением. Я предполагал, что в данном коде узкое место скорость чтения с накопителя. Чем можно объяснить такой результат теста?
0
Programming
Эксперт
39485 / 9562 / 3019
Регистрация: 12.04.2006
Сообщений: 41,671
Блог
21.05.2020, 23:27
Ответы с готовыми решениями:

Скорость записи и чтения данных с разных источников - из реестра и Json
Всем привет. Возникла необходимость где то хранить данные приложения. Было выбрано два варианта: Реестр JSON Из какого...

Создание тестов на скорость чтения и скорость записи
Проблемка такая - пишу простенький тест на скорость записи и скорость чтения, локальных и съемных дисков. Как написать тесты вроде...

Так ли важна в реальной жизни скорость записи SSD, или смотреть надо только на скорость чтения, а на запись пофигу
https://www.citilink.ru/catalog/computers_and_notebooks/hdd/ssd_in/420250/ вот у него скорость записи низкая, но он самый популярный

148
2736 / 891 / 331
Регистрация: 10.02.2018
Сообщений: 2,128
27.05.2020, 10:36
Студворк — интернет-сервис помощи студентам
bedvit, в некоторых источниках пишут, что адрес принимающего буфера то же должен быть выровнен по размеру сектора (File Buffering)
File access buffer addresses for read and write operations should be physical sector-aligned, which means aligned on addresses in memory that are integer multiples of the volume's physical sector size. Depending on the disk, this requirement may not be enforced.
C++
1
2
3
4
char* notalign = new char[nNumberOfBytesToRead+1+512]; //память
char* buf = notalign; //буфер
if (size_t(buf) % 512)
  buf += 512 - (size_t(buf) % 512);
1
 Аватар для bedvit
1210 / 261 / 22
Регистрация: 20.05.2016
Сообщений: 1,147
Записей в блоге: 22
27.05.2020, 12:27  [ТС]
Ygg, протестировал, разницы в скорости нет.
Добился асинхронности, добавив еще буфер. Скорость в ущерб памяти (выиграл 0,05 сек. на простом алгоритме, далее. думаю, выигрыш должен быть больше)
Функция подсчета строк в файле csv (ansi) - 2.54 сек. на 1ГБ.
C++
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
int GetRowsCountCSVansi(PCTSTR path)
{
    HANDLE  hFile;     // дескриптор файла
    HANDLE  hEndRead;  // дескриптор события
    OVERLAPPED  ovl;   // структура управления асинхронным доступом к файлу
    const DWORD  nNumberOfBytesToRead = 16777216;//8388608;//читаем в буфер байты
    //char* buf = new char[nNumberOfBytesToRead]; //буфер CreateFile
    char* notalign = new char[nNumberOfBytesToRead + 512]; //память
    char* buf = notalign; //буфер
    if (size_t(buf) % 512) { buf += 512 - (size_t(buf) % 512); }
 
    char* bufWork = new char[nNumberOfBytesToRead + 1]; //буфер Рабочий
    bufWork[0] = '\0';//добавим нуль-терминатор
    bufWork[nNumberOfBytesToRead] = '\0';//добавим нуль-терминатор
    char* find;// указатель для поиска
    int strCount = 1; //счетчик строк
 
    // создаем события с автоматическим сбросом
    hEndRead = CreateEvent(NULL, FALSE, FALSE, NULL);
    if (hEndRead == NULL) { return GetLastError(); }
 
    // инициализируем структуру OVERLAPPED
    ovl.Offset = 0;         // младшая часть смещения равна 0
    ovl.OffsetHigh = 0;      // старшая часть смещения равна 0
    ovl.hEvent = hEndRead;   // событие для оповещения завершения чтения
 
    // открываем файл для чтения
    hFile = CreateFile(
        path,   // имя файла
        GENERIC_READ,          // чтение из файла
        FILE_SHARE_READ,       // совместный доступ к файлу
        NULL,                  // защиты нет
        OPEN_EXISTING,         // открываем существующий файл
        FILE_FLAG_OVERLAPPED | FILE_FLAG_NO_BUFFERING,  // асинхронный ввод
        NULL                   // шаблона нет
    );
    // проверяем на успешное открытие
    if (hFile == INVALID_HANDLE_VALUE)
    {
        CloseHandle(hEndRead);
        delete[] notalign;
        delete[] bufWork;
        return -1;
    }
    // читаем данные из файла
    for (;;)
    {
        DWORD  dwBytesRead;
        DWORD  dwError;
        char* find = buf; //буфер
        // читаем одну запись
        if (!ReadFile(
            hFile,           // дескриптор файла
            buf,             // адрес буфера, куда читаем данные
            nNumberOfBytesToRead,// количество читаемых байтов
            &dwBytesRead,    // количество прочитанных байтов
            &ovl             // чтение асинхронное
        ))
        {
            switch (dwError = GetLastError())
            {
            case ERROR_IO_PENDING:
                // асинхронный ввод-вывод все еще происходит 
                // сделаем кое-что пока он идет 
                break;
            case ERROR_HANDLE_EOF:
                // мы достигли конца файла 
                // закрываем дескрипторы
                CloseHandle(hFile);
                CloseHandle(hEndRead);
                delete[] notalign;
                delete[] bufWork;
                return strCount;
            default:
                // закрываем дескрипторы
                CloseHandle(hFile);
                CloseHandle(hEndRead);
                delete[] notalign;
                delete[] bufWork;
                return -1;
            }
        }
        //работаем асинхронно, выполняем код, пока ждем чтение с диска//
        find = bufWork; //буфер
        find = strstr(find, "\n");
        while (find != NULL)
        {
            find = find + 1;
            strCount++;
            find = strstr(find, "\n");
        }
        //работаем асинхронно, выполняем код, пока ждем чтение с диска//
 
        // ждем, пока завершится асинхронная операция чтения
        WaitForSingleObject(hEndRead, INFINITE);
 
        // проверим результат работы асинхронного чтения 
        // если возникла проблема ... 
        if (!GetOverlappedResult(hFile, &ovl, &dwBytesRead, FALSE))
        {
            // решаем что делать с кодом ошибки
            switch (dwError = GetLastError())
            {
            case ERROR_HANDLE_EOF:
            {
                // мы достигли конца файла в ходе асинхронной операции  
                // закрываем дескрипторы
                CloseHandle(hFile);
                CloseHandle(hEndRead);
                delete[] notalign;
                delete[] bufWork;
                return strCount;
            }
            // решаем что делать с другими случаями ошибок
            CloseHandle(hFile);
            CloseHandle(hEndRead);
            delete[] notalign;
            delete[] bufWork;
            return -1;
            }// конец процедуры switch (dwError = GetLastError())
        }
 
        //синхронно копируем данные из буфера в рабочий буфер, далее с ним работаем асинхронно
        memcpy(bufWork, buf, nNumberOfBytesToRead);
 
        // увеличиваем смещение в файле
        ovl.Offset += nNumberOfBytesToRead;// sizeof(n);
    }
}
Добавлено через 30 минут
4 млн. строк

Добавлено через 19 минут
С этим вариантом более-менее ясно.
Остался еще вариант - через MMF (memory mapped files). Навряд ли быстрее, но видятся и плюсы - единое адресное пространство, не нужно сшивать строки из разных буферов и хранить где-то предыдущий буфер (если кусок найденной строки там остался), если сделать MMF сразу на весь файл. Однако, что будет с память, как это будет перевариваться? Не работал с MMF, возможно у кого-то есть наработки или информация.
0
264 / 153 / 33
Регистрация: 29.06.2019
Сообщений: 1,549
27.05.2020, 15:51
Цитата Сообщение от bedvit Посмотреть сообщение
через MMF (memory mapped files).
я когда сканила этот вариант - думала читать быстро (но такой вариант на малого объёма файлах выигрыша не даёт, поэтому оставила эту тему) - мне понравились линки:
mmap Tutorial
Системные вызовы mmap(), munmap()
кратко здесь
на stackoverflow - mmap() vs. reading blocks - статья была...
- но у меня тесты не проходили, т.к.
fatal error: sys/mman.h: No such file or directory
- нет заголовочника на компе...
p.s.
встречался и вариант boost
Memory-mapped files using the boost library
Headers
Code
1
<boost/iostreams/device/mapped_file.hpp>
- но тут уже руки не дошли...
код в сети был
C++
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
// [URL="https://stackoverflow.com/questions/23468227/memory-usage-when-using-boostiostreamsmapped-file"]link[/URL]
//code here which uses boost iostream to mmap & then writes to the mapped file:
typedef unordered_map<int, string> work;
 
    int main()
    {
 
            work d;
            d[0] = "a";
 
            boost::iostreams::mapped_file_params  params;
            params.path = "map.dat";
            params.new_file_size = 1000000000;
            params.mode = (std::ios_base::out | std::ios_base::in);
            boost::iostreams::mapped_file  mf;
            mf.open(params);
            work* w = static_cast<work*>((void*)mf.data());
            w[0] = d;
            for(int i=1; i <1000000000 ;++i)
            {
                    w->insert(std::make_pair(i, "abcdef"));
            }
            mf.close();
 
    }

... так я это и забросила, когда поняла, что не для моих мелких файлов это дело выгодно...
p.p.s
и здесь на кибере есть MMF - и без того нерабочего под винду заголовочника - он, наверно, под линукс был нужен (тот заголовочник)
0
18 / 16 / 0
Регистрация: 03.01.2018
Сообщений: 208
Записей в блоге: 1
27.05.2020, 16:23
bedvit, скорость чтения/записи зависит от расположения файла на диске и его фрагментация в случае с HDD. В случае с SSD скорость может очень сильно зависеть от количества свободного места на SSD.
0
 Аватар для bedvit
1210 / 261 / 22
Регистрация: 20.05.2016
Сообщений: 1,147
Записей в блоге: 22
01.06.2020, 00:14  [ТС]
Есть ли возможность узнать хеширован файл системой или нет? Логично предположить, что если хеширован, читать из хеша, нет отключать хеширование и читать с диска, т.е. выходит, таким образом - самый быстрый способ чтения.
0
18 / 16 / 0
Регистрация: 03.01.2018
Сообщений: 208
Записей в блоге: 1
01.06.2020, 20:45
bedvit, cache

hash
0
 Аватар для bedvit
1210 / 261 / 22
Регистрация: 20.05.2016
Сообщений: 1,147
Записей в блоге: 22
01.06.2020, 23:17  [ТС]
letsmail9, да, я имел ввиду кеш. Кеширование файловой системой.

Добавлено через 7 минут
Спасибо, не заметил очепятку...
0
 Аватар для bedvit
1210 / 261 / 22
Регистрация: 20.05.2016
Сообщений: 1,147
Записей в блоге: 22
04.06.2020, 18:16  [ТС]
Результат на примере функции поиска строки в CSV, по подстроке (конструктивные предложения принимаются)
Особенности реализации:
1.Асинхронность
2.Возможность отключения системного кеширования.
3.Возможность вывода всех найденных строк (или первой)
4.Строка выводится вся, в т.ч. с разделителями
5.Адрес принимающего буфера и рабочего выровнен по размеру сектора/страницы - 4096 байт
6.Использована структура _ULARGE_INTEGER, что прибавило 0,5 сек к времени выполнения (возможно чем-то заменить?)
7.В силу малого количества строк использовал std::string (думаю на скорости это не скажется)
Кликните здесь для просмотра всего текста
C++
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
int FindInCSVansi(PCTSTR path, const char* findStr, bool multiLine, bool NO_BUFFERING)
{
const DWORD  nNumberOfBytesToRead = 33554432; //16777216;//8388608;//читаем в буфер байты
char* notAlignBuf = new char[nNumberOfBytesToRead + 4096]; //буфер
char* buf = notAlignBuf; //буфер
if (size_t(buf) % 4096) { buf += 4096 - (size_t(buf) % 4096); }//адрес принимающего буфера тоже должен быть выровнен по размеру сектора/страницы 
 
char* notAlignBufBufWork = new char[nNumberOfBytesToRead + 4096 + 1]; //буфер Рабочий
char* bufWork = notAlignBufBufWork; //буфер
if (size_t(bufWork) % 4096) { bufWork += 4096 - (size_t(bufWork) % 4096); }//адрес рабочего буфера тоже выровнял по размеру сектора/страницы  
 
bufWork[0] = '\0';//добавим нуль-терминатор
bufWork[nNumberOfBytesToRead] = '\0';//добавим нуль-терминатор
char* find;// указатель для поиска
size_t strCount = 1; //счетчик строк
std::string strOut; //итоговая строка
DWORD  dwBytesReadWork;
DWORD findStatus = 0; //статус поиска
ULONGLONG ignoreOffset=0; //для расчета холостых циклов
bool ERR_HANDLE_EOF = false; //метка конца файла
 
// создаем события с автоматическим сбросом
HANDLE hEndRead = CreateEvent(NULL, FALSE, FALSE, NULL);// дескриптор события
if (hEndRead == NULL) { return GetLastError(); }
 
_ULARGE_INTEGER ui; //Представляет 64-разрядное целое число без знака обединяя два 32-х разрядных
ui.QuadPart = 0;
 
OVERLAPPED  ovl;   // структура управления асинхронным доступом к файлу// инициализируем структуру OVERLAPPED
ovl.Offset = 0;         // младшая часть смещения равна 0
ovl.OffsetHigh = 0;      // старшая часть смещения равна 0
ovl.hEvent = hEndRead;   // событие для оповещения завершения чтения
 
// открываем файл для чтения
HANDLE hFile = CreateFile(  // дескриптор файла
    path,   // имя файла
    GENERIC_READ,          // чтение из файла
    FILE_SHARE_READ,       // совместный доступ к файлу
    NULL,                  // защиты нет
    OPEN_EXISTING,         // открываем существующий файл
    FILE_FLAG_OVERLAPPED | (NO_BUFFERING ? FILE_FLAG_NO_BUFFERING : FILE_FLAG_RANDOM_ACCESS),// асинхронный ввод//отключаем системный буфер
    NULL                   // шаблона нет
);
// проверяем на успешное открытие
if (hFile == INVALID_HANDLE_VALUE)
{
    CloseHandle(hEndRead);
    delete[] notAlignBuf;
    delete[] notAlignBufBufWork;
    return -1;
}
// читаем данные из файла
for (;;)
{
    DWORD  dwBytesRead;
    DWORD  dwError;
    find = buf; //буфер
    // читаем одну запись
    if (!ReadFile(
        hFile,           // дескриптор файла
        buf,             // адрес буфера, куда читаем данные
        nNumberOfBytesToRead,// количество читаемых байтов
        &dwBytesRead,    // количество прочитанных байтов
        &ovl             // чтение асинхронное
    ))
    {
        switch (dwError = GetLastError())// решаем что делать с кодом ошибки
        {
            //эти ошибки смотрм после завершения асинхронной операции чтения, для возможности обработать рабочий буфер
        case ERROR_IO_PENDING: { break; }        // асинхронный ввод-вывод все еще происходит // сделаем кое-что пока он идет 
        case ERROR_HANDLE_EOF: { ERR_HANDLE_EOF = true; break; } // мы достигли конца файла читалкой ReadFile
        default: {goto return1; }// другие ошибки
        }
    }
    //работаем асинхронно, выполняем код, пока ждем чтение с диска//
    if (ui.QuadPart != ignoreOffset)//игнорируем первую итерацию и этерации прошедшие следующим буфером, когда нужно было вернутся в предыдущий буфер
    {
        bufWork[dwBytesReadWork] = '\0';//добавим нуль-терминатор
        find = bufWork; //новый буфер
    go_1:
        char* strStart = bufWork;
        char* strEnd = NULL;
 
        if (findStatus == 0)//goFind
        {
            find = strstr(find, findStr);
            if (find != NULL) //если нужная подстрока найдена
            {
 
                for (strStart = find; strStart >= bufWork; strStart--)
                {
                    if (*strStart == '\n') { break; } //если нашли начало строки
                }
                strStart++;
                if (ui.QuadPart == nNumberOfBytesToRead || strStart != bufWork)//если первый буфер или найдено начало строки
                {
                    strEnd = strchr(find, '\n');
                    if (strEnd != NULL) //если нашли конец строки
                    {
                        strOut = strOut + std::string(strStart, strEnd - strStart + 1);
                        if (!multiLine)
                        {
                            goto return0;
                        }
                        else//поиск в следующей строке начинаем с конца предыдущей
                        {
                            find = strEnd++;
                            findStatus = 0;
                            goto go_1;
                        }
                    }
                    else//если конец строки в следующем буфере или конец файла
                    {
                        strOut = strOut + std::string(strStart);
                        findStatus = 1;
                    }
                }
                else //если начало строки не найдено и это не первый буфер смотрим предыдущий буфер
                {
                    findStatus = 2;
                    ui.QuadPart -= nNumberOfBytesToRead * 3;
                    ignoreOffset = ui.QuadPart + nNumberOfBytesToRead;
                }
            } //если нужная подстрока найдена
        }
        else if (findStatus == 1)//goBuf
        {
            strEnd = strchr(find, '\n');
            if (strEnd != NULL) //если нашли конец строки
            {
                strOut = strOut + std::string(find, strEnd - find+1);
                findStatus = 0;
                if (!multiLine)
                {
                    goto return0;
                }
                else//поиск в следующей строке начинаем с конца предыдущей
                {
                    find = strEnd++;
                    findStatus = 0;
                    goto go_1;
                }
            }
            else//если конец строки в следующем буфере или конец файла
            {
                strOut = strOut + std::string(find);
            }
        }
        else if (findStatus == 2)//goBackBuf
        {
 
            for (strStart = bufWork + dwBytesReadWork; strStart >= bufWork; strStart--)
            {
                if (*strStart == '\n') { break; }//если нашли начало строки
            }
            strStart++;
            if (ui.QuadPart == nNumberOfBytesToRead || strStart != bufWork)//если первый буфер или найдено начало строки
            {
                find = strStart++;
                findStatus = 1;
                goto go_1;
            }
            else
            {
                ui.QuadPart -= nNumberOfBytesToRead * 3;//если начало строки не найдлено смотрим предыдущий буфер
                ignoreOffset = ui.QuadPart + nNumberOfBytesToRead;
            }
 
        }
    }
    if (ERR_HANDLE_EOF) { if (findStatus != 2) goto return0; }
    //работаем асинхронно, выполняем код, пока ждем чтение с диска//
 
    // ждем, пока завершится асинхронная операция чтения
    WaitForSingleObject(hEndRead, INFINITE);
 
    // проверим результат работы асинхронного чтения // если возникла проблема ... 
    if (!GetOverlappedResult(hFile, &ovl, &dwBytesRead, FALSE))
    {
        switch (dwError = GetLastError())// решаем что делать с кодом ошибки
        {
        case ERROR_HANDLE_EOF: 
        {
            if (findStatus != 2) goto return0; break;//если конец файла. но мы должны вернутся найти начало строки.
        }// мы достигли конца файла в ходе асинхронной операции
        default: {goto return1; }// другие ошибки
        }// конец процедуры switch (dwError = GetLastError())
    }
 
    //синхронно копируем данные из буфера в рабочий буфер, далее с ним работаем асинхронно
    memcpy(bufWork, buf, nNumberOfBytesToRead);
 
    // увеличиваем смещение в файле
    dwBytesReadWork = dwBytesRead;//кол-во считанных байт
    ui.QuadPart += nNumberOfBytesToRead; //добавляем смещение к указателю на файл
    ovl.Offset = ui.LowPart;// вносим смещение в младшее слово
    ovl.OffsetHigh = ui.HighPart;// вносим смещение в старшеее слово
}
 
return0:
// закрываем дескрипторы, освобождаем память
std::cout << "String Find " << strOut << std::endl;
CloseHandle(hFile);
CloseHandle(hEndRead);
delete[] notAlignBuf;
delete[] notAlignBufBufWork;
return 0;
return1:
CloseHandle(hFile);
CloseHandle(hEndRead);
delete[] notAlignBuf;
delete[] notAlignBufBufWork;
return -1;
}

Тайминги на 1ГБ (4 млн.строк) и 10ГБ (40 млн.строк) на двух дисках (HDD и SSD):
SSD: 1GB: NO_BUF: Time - 2.548000
SSD: 1GB: Time - 3.579000
SSD: 1GB: Хеширован: Time - 1.316000
SSD: 10GB: NO_BUF: Time - 25.452000
SSD: 10GB: Time - 35.779000
HDD: 1GB: NO_BUF: Time - 17.108000
HDD: 1GB: Time - 17.166000
HDD: 1GB: Хеширован: Time - 1.317000
HDD: 10GB: NO_BUF: Time - 206.368000
HDD: 10GB: Time - 219.626000

Выводы:
1.Если файл не влезает в системный кеш, отключение кеширования положительно влияет на тайминг, если влезает кеширование серьезно ускоряет процесс.
2.Ответом по теме (почему скорости равные на разных носителях) - кеширование системой рабочего файла. Как видим в тестах при кешировании результат очень похож, хотя по факту скорость чтения с двух носителей различается на порядок.
3.Асинхронное чтение явно ускоряет процесс.

Добавлено через 14 минут
4. При документированной скорости чтения SSD - 450 Мб/с, 10 ГБ читается 22,2 сек, что близко к скорости функции (25,45 сек). Возможно ли оптимизировать эти 3 сек. - наверное да, но вот нужно ли.

Добавлено через 53 минуты
5. Пока остановился на чтение блоками, кратно степени двойки - по 16 Мб. Обычно такой физический кеш в hdd. Есть, правда уже и 32, 64 и т.д.
0
Just Do It!
 Аватар для XLAT
4219 / 2680 / 656
Регистрация: 23.09.2014
Сообщений: 9,235
Записей в блоге: 3
07.06.2020, 17:09
bedvit,
Опять же, если вы посмотрите тему по ссылке выше - там будет реализация на std::string - это медленно.
так как я в прошлом тоже ассемблердрочер, то вопрос эффективности у меня не на последнем месте,
то решил проверить ваше утверждение на вашем же тесте:
C++
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
#include <string>
#include <fstream>
#include <ctime>
#include <iostream>
#include <exception>
#include <stdio.h>
 
const char* const NAMEFILE = "N1GB.txt";
 
///----------------------------------------------------------------------------|
/// Генератор файла большого размера.
///----------------------------------------------------------------------------:
void generator_testfile()
{   std::ifstream inf(NAMEFILE);
    if(inf.is_open())
    {   inf.close();
        std::cout << "file exist...\n";
        return;
    }
    std::ofstream f(NAMEFILE);
 
    const int N1GB = 1024*1024*4;
 
    for    (int i = 0; i < N1GB; i++)
    {   f << "qwerty1234567890qwerty1234567890qwerty1234567890qwerty1234567";
        f << "qwerty1234567890qwerty1234567890qwerty1234567890qwerty1234567";
        f << "qwerty1234567890qwerty1234567890qwerty1234567890qwerty1234567";
        f << "qwerty1234567890qwerty1234567890qwerty1234567890qwerty1234567\n";
    }
    f << "........................ASD123FGH456.........................\n";
    f.close();
}
 
int main()
{   setlocale(0, "");
    generator_testfile();
 
    clock_t t1;
    {   
    ////////////////////////////////////////////////////////////////////////---1
        t1 = clock();
        std::ifstream is;
        std::filebuf * fb = is.rdbuf();
        fb->open(NAMEFILE, std::ios::in | std::ios::binary);
        std::string str;
        str.reserve(1024);
        while (is)
        {   std::getline(is, str);
            size_t found = str.find("ASD123FGH456");
            if (found != std::string::npos)
            {   std::cout << "Find OK: " << std::endl;
            };
        }
        fb->close();
        printf("std::getline<ОПТ> + Find: Time - %f(школа)\n",
               (clock() - t1 + .0) / CLOCKS_PER_SEC); // время отработки
    }
 
 
    {   
    ////////////////////////////////////////////////////////////////////////---2
        t1 = clock();
        std::ifstream is;
        std::filebuf * fb = is.rdbuf();
        fb->open(NAMEFILE, std::ios::in | std::ios::binary);
        while (is)
        {   std::string str;
            std::getline(is, str);
        }
        fb->close();
        printf("std::getline            : Time - %f\n",
               (clock() - t1 + .0) / CLOCKS_PER_SEC); // время отработки
    }
 
    {
    ///-------------------------------------|
    /// Небольшая школьная оптимизация!     |
    ///-------------------------------------:
    ////////////////////////////////////////////////////////////////////////---3
        t1 = clock();
        std::ifstream is;
        std::filebuf * fb = is.rdbuf();
        fb->open(NAMEFILE, std::ios::in | std::ios::binary);
        std::string str;
        str.reserve(1024);
        while(is)
        {   std::getline(is, str);
        }
        fb->close();
        printf("std::getline<ОПТ>       : Time - %f(школа)\n",
               (clock() - t1 + .0) / CLOCKS_PER_SEC); // время отработки
    }
 
    {
    ////////////////////////////////////////////////////////////////////////---4
#pragma warning(disable : 4996)
        t1 = clock();
        char str[33000];
        char* estr;
        FILE* file;
        file = fopen(NAMEFILE, "r");
        if (file != NULL)
        {   while (1)
            {   estr = fgets(str, sizeof(str), file);
                if (estr == NULL)
                {   break;
                }
            }
            fclose(file);
        }
        printf("fgets                   : Time - %f\n",
               (clock() - t1 + .0) / CLOCKS_PER_SEC); // время отработки
    }
 
    std::cout << "\n\n";
    system("pause");
    return 0;
}


bedvit, извините, но у вас фейк.
0
 Аватар для bedvit
1210 / 261 / 22
Регистрация: 20.05.2016
Сообщений: 1,147
Записей в блоге: 22
07.06.2020, 19:33  [ТС]
XLAT, это старый тест, fgets() - тоже медленно.
вот тестовый стенд:
C++
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
int createfile() //создание файла для теста
{
    const int N = 40000000; // количество строк в файле
    std::ofstream fout("C:\\CSV_10_GB.csv");
    std::string str = "Пятый; Тридацть четвертый; 44221100; BBB; CIFRAPOLE; POLEPVTRETYE; ODIN ODIN - TRI; CC; 01.01.2013; 01.01.2013; 8963; 2UTY39ADVGKR; СU707039; 40200М У0026034; -; 11; 2; 150; 1998; 1; -; 21; 1980; 1490; НОМЕР ПЯТЬ; -; ПОЛЕ ОДИН; ПОЛЕ ОДИН ПЯТЬ; -; -";
    if (!fout){ return 1; }
 
    for (int i = 0; i < N; ++i)
    {
        fout << i + 1;
        fout << str << std::endl;
    }
    fout.close();
    return 0;
}
У меня оперативки 8 Гб, в кеш не влезет 10ГБ, если у вас больше, давайте увечим размер? для чистоты эксперимента.
теперь нужно найти 40 млн. строку (считаем от началf файла), теперь давайте сравним с вашим.
0
 Аватар для bedvit
1210 / 261 / 22
Регистрация: 20.05.2016
Сообщений: 1,147
Записей в блоге: 22
07.06.2020, 19:56  [ТС]
XLAT, вот результаты тестов на 1ГБ (кешировано)
Итого моя реализация быстрее вашей в 8 раз. У кого фейк?
Миниатюры
Скорость чтения csv с разных носителей  
0
Just Do It!
 Аватар для XLAT
4219 / 2680 / 656
Регистрация: 23.09.2014
Сообщений: 9,235
Записей в блоге: 3
08.06.2020, 04:37
Цитата Сообщение от bedvit Посмотреть сообщение
Итого моя реализация быстрее вашей в 8 раз. У кого фейк?
а щас у вас манипуляция.

вы предлагаете мне почистить ваш FindInCSVansi от char* к принудительному использованию std::string ???
Может вы сами это сделаете?
0
 Аватар для bedvit
1210 / 261 / 22
Регистрация: 20.05.2016
Сообщений: 1,147
Записей в блоге: 22
08.06.2020, 09:08  [ТС]
XLAT, я вам ничего не предлагаю, я лишь думаю что работа через указатели напряму с памятью быстрее, чем работа с классом строк std. По крайней мере не медленнее. Здесь и адресная арифметика, и в любое место или с нужного места я могу писать то что мне захочется. Std здесь меня ограничивает. Зачем он мне?
1
фрилансер
 Аватар для Алексей1153
6495 / 5724 / 1133
Регистрация: 11.10.2019
Сообщений: 15,286
08.06.2020, 09:37
bedvit, std::string делает и позволяет делать всё абсолютно то же самое, что и "сырой" массив, нет никаких ограничений

bedvit, насчёт асинхронности: то есть, делается превентивный запрос блока из файла, а в то время, пока он вычитывается, работаем с предыдущим блоком - я правильно понимаю?

в этом (в асинхронности) в нашем случае нет смысла, потому что поиск строки в озу (то есть, в уже загруженном блоке) , в блоке с детским размером 4096 происходит мгновенно по сравнению с чтением данных с диска.

Можно оставить только синхронное чтение - это не повлияет на скорость работы алгоритма (какие-то там условные 0.001% ускорения не рассматриваем). Убираем асинхронность ->> сильно упрощаем логику кода

Мне кажется, будет эффективнее заюзать OpenMP и начать искать сразу в нескольких равноудалённых местах файла
0
 Аватар для bedvit
1210 / 261 / 22
Регистрация: 20.05.2016
Сообщений: 1,147
Записей в блоге: 22
08.06.2020, 10:43  [ТС]
Цитата Сообщение от Алексей1153 Посмотреть сообщение
std::string делает и позволяет делать всё абсолютно то же самое, что и "сырой" массив, нет никаких ограничений
Алексей1153, c char* я делаю следующее:
выравнивание адреса
C++
1
if (size_t(buf) % 4096) { buf += 4096 - (size_t(buf) % 4096); }
поиск нужных подстрок
C++
1
2
3
4
5
find = strstr(find, findStr);
...
find ++;
...
find = strstr(find, findStr);
запись с нужной позиции, в нужную
C++
1
memcpy(bufWork+3, buf++, nNumberOfBytesToRead);
Как это сделать с std::string?
Цитата Сообщение от Алексей1153 Посмотреть сообщение
, в блоке с детским размером 4096
блок размером 16777216 байт.
Кроме того, кроме поиска там и формирование строки. это хоть небольшое, но время больше чем memcpy в рабочий буфер, поэтому профит есть.
Цитата Сообщение от Алексей1153 Посмотреть сообщение
эффективнее заюзать OpenMP
не уверен, здесь все упирается в скорость чтения с диска (буфера), а она и так приближена к документируемой.
0
фрилансер
 Аватар для Алексей1153
6495 / 5724 / 1133
Регистрация: 11.10.2019
Сообщений: 15,286
08.06.2020, 11:04
Цитата Сообщение от bedvit Посмотреть сообщение
выравнивание адреса
то есть, хочется получить блок памяти, адрес которого начинается с определённой границы?
0
 Аватар для bedvit
1210 / 261 / 22
Регистрация: 20.05.2016
Сообщений: 1,147
Записей в блоге: 22
08.06.2020, 11:09  [ТС]
Алексей1153, да это обязательно.
Используется флаг FILE_FLAG_NO_BUFFERING в CreateFile()
https://docs.microsoft.com/ru-... -buffering
0
фрилансер
 Аватар для Алексей1153
6495 / 5724 / 1133
Регистрация: 11.10.2019
Сообщений: 15,286
08.06.2020, 11:21
bedvit, как-то так

C++
1
2
3
4
5
6
    const size_t block_size=4096;
 
    const DWORD nNumberOfBytesToRead = 3355443;
    std::string s(nNumberOfBytesToRead+block_size*2,'\0'); //*2 - это для учёта случая, когда размер меньше block_size
 
    char* buf_aligned=&s[block_size-size_t(s.c_str())%block_size];
Добавлено через 1 минуту
само собой, нельзя будет допускать переаллокации s, иначе buf_aligned станет невалиден

то есть, std::string в данном случае используется полностью обычный блок памяти, выделенный new

плюс единственный - не нужно заниматься ручным выделением памяти и освобождением

Добавлено через 1 минуту
наверное, можно что-то поколдовать над аллокатором, но я пока ещё не умею
https://en.cppreference.com/w/... sic_string
https://en.cppreference.com/w/cpp/memory/allocator
После сильного колдунства никаких ручных выравнивание делать не придётся. Но это уже лишнее, так как всё равно используем класс в качестве массива на куче

P.S. имя надо задать не "s", а что-то более говорящее. У меня - для примера
2
 Аватар для bedvit
1210 / 261 / 22
Регистрация: 20.05.2016
Сообщений: 1,147
Записей в блоге: 22
08.06.2020, 11:25  [ТС]
Алексей1153, спасибо за код, познавательно.
А два других случая, поиск и запись с нужной позиции?
0
фрилансер
 Аватар для Алексей1153
6495 / 5724 / 1133
Регистрация: 11.10.2019
Сообщений: 15,286
08.06.2020, 11:28
пользоваться встроенными функциями поиска, однако, можно - но придётся задавать смещение начала. Это в функциях std::string предусмотрено

Добавлено через 1 минуту
запись - тоже можно, если при этом не менять размер.

buf_aligned - указатель на начало выровненного блока, nNumberOfBytesToRead - длина блока. Дальше всё как обычно

C++
1
buf_aligned[7]=0;//7<nNumberOfBytesToRead
0
Надоела реклама? Зарегистрируйтесь и она исчезнет полностью.
inter-admin
Эксперт
29715 / 6470 / 2152
Регистрация: 06.03.2009
Сообщений: 28,500
Блог
08.06.2020, 11:28

Программа для чтения csv
Нужно прочитать csv-файл, записать в массив и потом делать с ним всякие манипуляции и сортировки (ну это как раз самое простое). У меня...

Скорость импорта CSV
Здравствуйте. Прислали мне CSV-шник размером 10 гигов, чтобы сделать пару запросов. Поставил я себе MySQL Workbench, подготовил...

Bat - файл для чтения только одной строки из файла CSV
Добрый день! Подскажите пожалуйста как сделать батник чтобы он выводил только одну (2, 3, 4,...) строку из файла CSV. На текущий...

Как правильно написать скрипт для чтения файла с расширением .csv(Excel) ?
как правильно написать скрипт для чтения файла с расширением .csv(Excel) пишу вот такой програмный код: &lt;? $count = 1; $file =...

Скорость чтения/записи
Всем привет! Есть ли какие-нибудь библиотеки в Java, чтобы узнать скорость чтения/записи на диск? Интересует именно Java, потому что...


Искать еще темы с ответами

Или воспользуйтесь поиском по форуму:
60
Ответ Создать тему
Новые блоги и статьи
Море в объективе
kumehtar 06.07.2026
Хотел написать много проникновенных слов, но передумал. Оно само - говорит. Кто слышит тот слышит.
Doom для терминала без стрельбы и монстров. 3D Raycasting на ascii.
dcc0 05.07.2026
Попросил нейронную сеть deepai. org написать рейкастинг 3D с библиотекой ncurses для Linux. Чтобы можно было ходить на стрелочки. Чтобы стены были отрисованы символами. Справилась. Первый вариант. . .
Установка статуса документа по условию
Maks 05.07.2026
Алгоритм из решения ниже реализован на нетиповом документе "НарядПутевка" разработанного в КА2. Задача: в табличной части "Материалы" документа при записи автоматически устанавливать статус. . .
Сезонность и суточность закисления почв
anaschu 04.07.2026
200 часов это все равно моловато. Есть ситуации, но нестандартные, когда смена происходит за 5 лет. Но обычно это 50 лет и более. Наверное, закисление почвы происходит сезонно в средней. . .
В чем ценность человеческого опыта в глобальном смысле?
kumehtar 03.07.2026
Возможно, ценность человека не в том, что он однажды достигает мудрости, а в том, что он становится носителем карты пути. Он знает не только истину, но и последовательность внутренних изменений,. . .
интеграция AnyLogic с самописным REST API и переход на Odoo
anaschu 03.07.2026
Успешная интеграция AnyLogic с самописным REST API и переход на промышленную Odoo WMS Сегодня проделал огромный путь от простой симуляции физических процессов до построения полноценной. . .
Поиск всех путей на ориентированном графе. Linux
dcc0 02.07.2026
Переработка старого кода из моей статьи. Через несколько переработок от PHP кода к C89 (надеюсь, 89). Но довольно запутанно получилось. Код для Linux. Но если убрать time и то, что с ним. . .
Сам себя обучал rest api
anaschu 02.07.2026
Педагогический лайфхак: Почему чистый REST API для ученика намного круче, чем готовые библиотеки Когда мы отказались от капризного JAR-файла AnyLogic и переписали код на стандартный HttpClient,. . .
КиберФорум - форум программистов, компьютерный форум, программирование
Powered by vBulletin
Copyright ©2000 - 2026, CyberForum.ru