Как ускорить загрузку 15М целых чисел из файлового потока?

function setCookie(cname,cvalue,exdays) {
    var d = new Date();
    d.setTime(d.getTime() + (exdays*24*60*60*1000));
    var expires = "expires=" + d.toGMTString();
    document.cookie = cname+"="+cvalue+"; "+expires;
}

function getCookie(cname) {
    var name = cname + "=";
    var ca = document.cookie.split(';');
    for(var i=0; i<ca.length; i++) {
        var c = ca[i];
        while (c.charAt(0)==' ') c = c.substring(1);
        if (c.indexOf(name) == 0) {
            return c.substring(name.length, c.length);
        }
    }
    return "";
}

function checkCookie() {
    var user=getCookie("username");
    if (user != "") {
        alert("Welcome again " + user);
    } else {
       user = prompt("Please enter your name:","");
       if (user != "" && user != null) {
           setCookie("username", user, 30);
       }
    }
}
13
задан drumsta 5 August 2010 в 16:39
поделиться

8 ответов

У вас есть две проблемы, касающиеся скорости операций записи и чтения.

Во-первых, std::copy не может выполнить оптимизацию блочного копирования при записи в выходной_итератор, потому что у него нет прямого доступа к базовой цели.

Во-вторых, вы записываете целые числа в формате ascii, а не в двоичном, поэтому на каждой итерации записи output_iterator создает ascii представление вашего int, а на чтении ему приходится разбирать текст обратно в целые числа. Я считаю, что это основная проблема производительности.

Необработанное хранилище вашего массива (при условии, что int состоит из 4 байт) должно быть всего 60 МБ, но поскольку каждый символ целого числа в ascii равен 1 байту, любые int с более чем 4 символами будут больше, чем двоичное хранилище, отсюда и ваш файл размером 130 МБ.

Не существует простого способа решить проблему скорости портативно (чтобы файл можно было читать на разных машинах с эндианом или интом) или при использовании std::copy. Самый простой способ - просто сбросить весь массив на диск, а затем прочитать его обратно, используя fstream.write и read, только помните, что это не совсем переносимо.

Для записи:

std::fstream out(config.c_str(), ios::out | ios::binary);
out.write( keys.data(), keys.size() * sizeof(int) );

И для чтения:

std::fstream in(config.c_str(), ios::in | ios::binary);
in.read( keys.data(), keys.size() * sizeof(int) );

----Update----

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

Возможно, что-то вроде этого:

std::array<int, 15000000> keys;

// data.txt are the ascii values and data.bin is the binary version
if(!file_exists("data.bin")) {
    std::ifstream in("data.txt");
    std::copy(std::istream_iterator<int>(in),
         std::istream_iterator<int>(), keys.begin());
    in.close();

    std::fstream out("data.bin", ios::out | ios::binary);
    out.write( keys.data(), keys.size() * sizeof(int) );
} else {
    std::fstream in("data.bin", ios::in | ios::binary);
    in.read( keys.data(), keys.size() * sizeof(int) );
}

Если у вас есть процесс установки, эта предварительная обработка также может быть выполнена в это время...

12
ответ дан 1 December 2019 в 19:39
поделиться

если целые числа сохраняются в двоичном формате и вас не интересуют проблемы с порядком байтов, попробуйте прочитать весь файл в память сразу (fread) и приведите указатель к int *

6
ответ дан 1 December 2019 в 19:39
поделиться

Вы можете предварительно скомпилировать массив в файл .o, который не нужно будет перекомпилировать, если данные не изменятся.

thedata.hpp:

static const int NUM_ENTRIES = 5;
extern int thedata[NUM_ENTRIES];

thedata.cpp:

#include "thedata.hpp"
int thedata[NUM_ENTRIES] = {
10
,200
,3000
,40000
,500000
};

Чтобы скомпилировать это:

# make thedata.o

Тогда ваше основное приложение будет выглядеть примерно так:

#include "thedata.hpp"
using namespace std;
int main() {
  for (int i=0; i<NUM_ENTRIES; i++) {
    cout << thedata[i] << endl;
  }
}

Предполагая, что данные не меняются часто, и что вы можете обрабатывать данные для создания thedata.cpp, то это фактически мгновенная загрузка. Я не знаю, захлебнется ли компилятор таким большим литеральным массивом!

6
ответ дан 1 December 2019 в 19:39
поделиться

Если числа никогда не меняются, предварительно обработайте файл в исходный код C ++ и скомпилируйте его в приложение.

Если число может измениться, и, следовательно, вы должны сохранить их в отдельном файле, который вы должны загрузить при запуске, не используйте это число по номеру, используя потоки ввода-вывода C ++. Потоки ввода-вывода C ++ - хорошая абстракция, но их слишком много для такой простой задачи, как быстрая загрузка группы чисел. По моему опыту, огромная часть времени выполнения тратится на анализ чисел, а другая - на доступ к файлу char с помощью char.

(Предполагая, что ваш файл состоит из более чем одной длинной строки.) Прочтите файл построчно, используя std :: getline () , проанализируйте числа из каждой строки, используя не потоки, а std: : strtol () . Это позволяет избежать огромных накладных расходов. Вы можете увеличить скорость потоков, создав свой собственный вариант std :: getline () , который считывает ввод вперед (с помощью istream :: read () ); стандартный std :: getline () также считывает входной char по char.

1
ответ дан 1 December 2019 в 19:39
поделиться

Внимание. Проверка реальности:

Чтение целых чисел из большого текстового файла является операцией, связанной с вводом-выводом, если вы не делаете что-то совершенно неправильно (например, используете для этого потоки C ++). Загрузка 15M целых чисел из текстового файла занимает менее 2 секунд на AMD64 @ 3GHZ, когда файл уже буферизован (и немного дольше, если нужно было получить с достаточно быстрого диска).Вот быстрая и грязная процедура, чтобы доказать мою точку зрения (поэтому я не проверяю все возможные ошибки в формате целых чисел и не закрываю свои файлы в конце, потому что я все равно exit ()).

$ wc nums.txt
 15000000  15000000 156979060 nums.txt

$ head -n 5 nums.txt
730547560
-226810937
607950954
640895092
884005970

$ g++ -O2 read.cc
$ time ./a.out <nums.txt
=>1752547657

real    0m1.781s
user    0m1.651s
sys     0m0.114s

$ cat read.cc 
#include <stdio.h>
#include <stdlib.h>
#include <ctype.h>
#include <vector>

int main()
{
        char c;
        int num=0;
        int pos=1;
        int line=1;
        std::vector<int> res;
        while(c=getchar(),c!=EOF)
        {
                if (c>='0' && c<='9')
                        num=num*10+c-'0';
                else if (c=='-') 
                        pos=0;
                else if (c=='\n')
                {
                        res.push_back(pos?num:-num);
                        num=0;
                        pos=1;
                        line++;
                }
                else
                {
                        printf("I've got a problem with this file at line %d\n",line);
                        exit(1);
                }
        }
        // make sure the optimizer does not throw vector away, also a check.
        unsigned sum=0;
    for (int i=0;i<res.size();i++) 
    {
    sum=sum+(unsigned)res[i];
    }
    printf("=>%d\n",sum); 
}

ОБНОВЛЕНИЕ: и вот мой результат при чтении текстового файла (не двоичного) с использованием кода mmap:

$ g++ -O2 mread.cc
$ time ./a.out nums.txt
=>1752547657

real    0m0.559s
user    0m0.478s
sys     0m0.081s

в pastebin:

Что я предлагаю

1-2 секунды - реалистичная нижняя граница для типичной настольной машины для загрузки этих данных. 2 минуты больше похоже на считывание микроконтроллером 60 МГц с дешевой SD-карты. Итак, либо у вас есть необнаруженное / не упомянутое аппаратное состояние , либо ваша реализация потока C ++ каким-то образом сломана или непригодна для использования. Я предлагаю установить нижнюю границу для этой задачи на вашем компьютере, запустив мой пример кода.

6
ответ дан 1 December 2019 в 19:39
поделиться

Используйте буфер из 1000 (или даже 15 МБ, вы можете изменять этот размер по своему усмотрению) целых чисел, а не целого числа после целого. Я считаю, что проблема не в использовании буфера.

0
ответ дан 1 December 2019 в 19:39
поделиться

Если данные в файле двоичные, и вам не нужно беспокоиться о порядке байтов, и вы работаете в системе, которая его поддерживает, используйте системный вызов mmap . См. Эту статью на веб-сайте IBM:

Высокопроизводительное сетевое программирование, Часть 2: Ускорение обработки как на клиенте, так и на сервере

Также см. Это сообщение SO:

Когда мне следует использовать mmap для доступа к файлам?

0
ответ дан 1 December 2019 в 19:39
поделиться

Сохраните файл в двоичном формате.

Запишите файл, взяв указатель на начало массива int и преобразуя его в указатель char . Затем запишите в файл символы 15000000 * sizeof (int) .

И когда вы читаете файл, сделайте то же самое в обратном порядке: прочтите файл как последовательность символов, возьмите указатель на начало последовательности и преобразуйте его в int * .

Конечно, это предполагает, что порядок байтов не является проблемой.

Для фактического чтения и записи файла отображение памяти, вероятно, является наиболее разумным подходом.

3
ответ дан 1 December 2019 в 19:39
поделиться
Другие вопросы по тегам:

Похожие вопросы: