Подтвердить что ты не робот

Самый быстрый способ чтения числовых значений из текстового файла в С++ (в этом случае двойной)

В настоящее время мой код выглядит следующим образом:

void ReadFile(double Cst[][1000], char* FileName, int height)

FILE* ifp;
double value;
int nRead = 0;
int mRead = 0;

//open the file, check if successful
ifp = fopen( FileName, "r" );
if (ifp==NULL){
    ...
}


for (nRead = 0; nRead < height; nRead++){
    for (mRead = 0; mRead < 1000; mRead++){
        fscanf(ifp, "%le",&value);
        Cst[nRead][mRead]=value;
    }
}

fclose(ifp);

Что я могу изменить, чтобы сделать его максимально быстрым?

4b9b3361

Ответ 1

atof, вероятно, намного быстрее, ему не нужно обрабатывать строку формата.

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


Я только что вспомнил проблему с atof - она ​​не говорит вам, где число закончилось, поэтому чтение нескольких номеров в последовательности затруднено. strtod лучше в этом отношении.

Ответ 2

Boost. Spirit. QI поставляется с эталон, который сравнивает производительность std::atof, std::strtod и boost::spirit::qi::double_. Вот результаты моей системы, используя VС++ 2010 SP1 x64 и Boost 1.46.1:

atof_test: 4,1579 секунды
strtod_test: 4,2339 секунд
spirit_qi_double_test: 1.2822 секунды

Это ставит Spirit.QI на на 230% быстрее, чем следующая самая быстрая проверяемая опция * и на 224% быстрее чем следующий самый быстрый непроверяемый параметр – довольно быстро, я бы сказал!

* В отличие от std::atof, std::strtod и Boost.Spirit сообщит вам, был ли вход действительным.


Обновление: Я перезапустил тест, дополнительно используя Boost. Spirit. X3 boost::spirit::x3::double_; вот результаты моей нынешней системы, с помощью VС++ 2015 Update 3 x64 и Boost 1.61.0:

atof_test: 2,2874 секунд
strtod_test: 2,2923 секунды
spirit_qi_double_test: 0,4849 секунд
spirit_x3_double_test: 0,4308 секунд

Это ставит Spirit.QI на 373% быстрее, чем следующий самый быстрый проверенный вариант и на 372% быстрее, чем следующий самый быстрый непроверяемый вариант, а Spirit.X3 на на 432% быстрее, чем следующий самый быстрый проверенный вариант и на 431% быстрее, чем следующий самый быстрый непроверяемый параметр – вещи значительно улучшились для Spirit, и, кроме того, код на основе X3 компилируется примерно в & frac15; времени, как код на основе QI, так и побеждает вокруг!

Кроме того, я проверил код в @Potatoswatter answer (изменен с таблицей экспонент с двойной точностью и поддержкой отрицательных чисел (code)), @6502 answer и @Mehrdad answer, с той же средой сборки и тестирования. Вот результаты (исключен код @6502, поскольку половина моих входов образца использует научную нотацию, которую его код не поддерживает):

potatoswatter_test: 0,2358 секунд
mehrdad_test: 0.3415 секунд

Если все входы преобразуются в фиксированную нотацию, мы можем также проверить код @6502:

atof_test: 3,6249 секунд
strtod_test: 3,7023 секунды
spirit_qi_double_test: 1.0763 секунды
spirit_x3_double_test: 2,3657 секунд
potatoswatter_test: 0,8347 секунд
6502_test: 4,1463 секунды
mehrdad_test: 1.3471 секунд

Одна интересная нота: QI не разбирает некоторые очень длинные входы фиксированной нотации; X3 правильно их анализирует, но работает значительно медленнее, чем с короткими вводами научной информации.

Ответ 3

Например, здесь очень быстрый парсер чисел из одного из моих проектов. Он обрабатывает только очень небольшое подмножество фактических функций численного разбора стандартной библиотеки.

uint64_t mystrtol( char *&pen, uint64_t val = 0 ) {
    for ( char c; ( c = *pen ^ '0' ) <= 9; ++ pen ) val = val * 10 + c;
    return val;
}

value_t mystrtof( char *&pen ) {
    static value_t const exp_table[]
     = { 1e5, 1e4, 1e3, 1e2, 10, 1, 0.1, 1e-2, 1e-3, 1e-4, 1e-5, 1e-6, 1e-7, 1e-8, 1e-9, 1e-10, 1e-11, 1e-12, 1e-13, 1e-14, 1e-15, 1e-16, 1e-17 },
     * exp_lookup = & exp_table[ 5 ];

    while ( iswspace( * ++ pen ) ) ;
    //if ( *pen == '-' ) ++ pen; // don't think we ever care about negative numbers
    uint64_t val = mystrtol( pen );
    int neg_exp = 0;
    if ( *pen == '.' ) { // mainly happens when val = 0
        char const *fracs = ++ pen;
        val = mystrtol( pen, val );
        neg_exp = pen - fracs;
    }
    if ( ( *pen | ('E'^'e') ) == 'e' ) {
        neg_exp += *++pen == '-'? mystrtol( ++ pen ) : - mystrtol( ++ pen );
    }
    return val * exp_lookup[ neg_exp ];
}

Ответ 4

C/С++ парсинг чисел из текста очень медленный. Потоки ужасающе медленны, но даже синтаксический анализ числа C медленный, потому что довольно сложно получить его с точностью до последнего бита точности.

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

Если вам не нужен показатель экспоненты и точность этой функции достаточно, это код анализатора, аналогичный тому, который я написал тогда. На моем ПК он теперь в 6,8 раза быстрее, чем strtod и в 22,6 раза быстрее, чем sstream.

double parseFloat(const std::string& input)
{
    const char *p = input.c_str();
    if (!*p || *p == '?')
        return NAN_D;
    int s = 1;
    while (*p == ' ') p++;

    if (*p == '-') {
        s = -1; p++;
    }

    double acc = 0;
    while (*p >= '0' && *p <= '9')
        acc = acc * 10 + *p++ - '0';

    if (*p == '.') {
        double k = 0.1;
        p++;
        while (*p >= '0' && *p <= '9') {
            acc += (*p++ - '0') * k;
            k *= 0.1;
        }
    }
    if (*p) die("Invalid numeric format");
    return s * acc;
}

Ответ 5

Быстрый способ - выделить текстовый буфер или строку, прочитать как можно больше в строке, а затем проанализировать строку.

Ваше первое узкое место - это ввод-вывод файлов. Второй (по порядку) преобразование текста в числа. Вы должны профилировать свою программу относительно того, быстрее ли sscanf или std::istringstream. Изменения в области ввода-вывода приведут к самым большим изменениям производительности.

Сделать процесс еще быстрее, используя несколько потоков и двойную буферизацию. Один поток считывает данные в один или несколько буферов, в то время как другой поток анализирует данные из этих буферов.

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

Ответ 6

Для С++ работа с потоками намного проще и почти всегда намного медленнее, чем использование C-интерфейсов. Однако я подозреваю, что скорость различных C-интерфейсов будет зависеть от их реализации. atof может быть быстрее, чем strtod на одной платформе, а медленнее - на другой.

Лично я бы посмотрел на быстрые способы чтения файла, а не на быстрые способы разбора парных. И ваши самые быстрые способы чтения файлов - это почти всегда API-интерфейсы на платформе (файлы с отображением памяти, разброс/сбор ввода-вывода и т.д.). Поэтому очень сложно дать вам ответ, который будет самым быстрым способом, потому что эта платформа специфична и изменится в будущем.

Ответ 7

Мне кажется, вы написали много хрупкого кода в надежде, что он будет суперэффективным. Прежде чем пытаться выполнить весь этот код, попробовали ли вы простое идиоматическое решение на С++ и решили, что он недостаточно быстрый?

std::ifstream input("/path/to/file");
if ( !input.is_open() ) {
    // handle error.
}
std::vector<double> numbers;
std::copy(std::istream_iterator<double>(input),
    std::istream_iterator(), std::back_inserter(numbers));

// Access item at position (i,j).
double x = numbers[1000*j+i];

Имейте в виду, что разработчики, стоящие за вашей реализацией стандартной библиотеки, изо всех сил стараются сделать этот простой код как можно быстрее. Скорее всего, вы сможете достичь своих требований к производительности с помощью этой тривиальной части кода.

Кроме того, вы получаете кучу халявы:

  • автоматически очищается (управляет памятью и дескриптором файла);
  • автоматически изменяет размер для больших входов;
  • проверяет наличие ошибок при разборе.

Ответ 8

Моя версия, которая также обрабатывает экспоненты:

template<class It>
double mystrtod(It &s, It const end)
{
    static double const pow10[] = { 1E-323, 1E-322, 1E-321, 1E-320, 1E-319, 1E-318, 1E-317, 1E-316, 1E-315, 1E-314, 1E-313, 1E-312, 1E-311, 1E-310, 1E-309, 1E-308, 1E-307, 1E-306, 1E-305, 1E-304, 1E-303, 1E-302, 1E-301, 1E-300, 1E-299, 1E-298, 1E-297, 1E-296, 1E-295, 1E-294, 1E-293, 1E-292, 1E-291, 1E-290, 1E-289, 1E-288, 1E-287, 1E-286, 1E-285, 1E-284, 1E-283, 1E-282, 1E-281, 1E-280, 1E-279, 1E-278, 1E-277, 1E-276, 1E-275, 1E-274, 1E-273, 1E-272, 1E-271, 1E-270, 1E-269, 1E-268, 1E-267, 1E-266, 1E-265, 1E-264, 1E-263, 1E-262, 1E-261, 1E-260, 1E-259, 1E-258, 1E-257, 1E-256, 1E-255, 1E-254, 1E-253, 1E-252, 1E-251, 1E-250, 1E-249, 1E-248, 1E-247, 1E-246, 1E-245, 1E-244, 1E-243, 1E-242, 1E-241, 1E-240, 1E-239, 1E-238, 1E-237, 1E-236, 1E-235, 1E-234, 1E-233, 1E-232, 1E-231, 1E-230, 1E-229, 1E-228, 1E-227, 1E-226, 1E-225, 1E-224, 1E-223, 1E-222, 1E-221, 1E-220, 1E-219, 1E-218, 1E-217, 1E-216, 1E-215, 1E-214, 1E-213, 1E-212, 1E-211, 1E-210, 1E-209, 1E-208, 1E-207, 1E-206, 1E-205, 1E-204, 1E-203, 1E-202, 1E-201, 1E-200, 1E-199, 1E-198, 1E-197, 1E-196, 1E-195, 1E-194, 1E-193, 1E-192, 1E-191, 1E-190, 1E-189, 1E-188, 1E-187, 1E-186, 1E-185, 1E-184, 1E-183, 1E-182, 1E-181, 1E-180, 1E-179, 1E-178, 1E-177, 1E-176, 1E-175, 1E-174, 1E-173, 1E-172, 1E-171, 1E-170, 1E-169, 1E-168, 1E-167, 1E-166, 1E-165, 1E-164, 1E-163, 1E-162, 1E-161, 1E-160, 1E-159, 1E-158, 1E-157, 1E-156, 1E-155, 1E-154, 1E-153, 1E-152, 1E-151, 1E-150, 1E-149, 1E-148, 1E-147, 1E-146, 1E-145, 1E-144, 1E-143, 1E-142, 1E-141, 1E-140, 1E-139, 1E-138, 1E-137, 1E-136, 1E-135, 1E-134, 1E-133, 1E-132, 1E-131, 1E-130, 1E-129, 1E-128, 1E-127, 1E-126, 1E-125, 1E-124, 1E-123, 1E-122, 1E-121, 1E-120, 1E-119, 1E-118, 1E-117, 1E-116, 1E-115, 1E-114, 1E-113, 1E-112, 1E-111, 1E-110, 1E-109, 1E-108, 1E-107, 1E-106, 1E-105, 1E-104, 1E-103, 1E-102, 1E-101, 1E-100, 1E-099, 1E-098, 1E-097, 1E-096, 1E-095, 1E-094, 1E-093, 1E-092, 1E-091, 1E-090, 1E-089, 1E-088, 1E-087, 1E-086, 1E-085, 1E-084, 1E-083, 1E-082, 1E-081, 1E-080, 1E-079, 1E-078, 1E-077, 1E-076, 1E-075, 1E-074, 1E-073, 1E-072, 1E-071, 1E-070, 1E-069, 1E-068, 1E-067, 1E-066, 1E-065, 1E-064, 1E-063, 1E-062, 1E-061, 1E-060, 1E-059, 1E-058, 1E-057, 1E-056, 1E-055, 1E-054, 1E-053, 1E-052, 1E-051, 1E-050, 1E-049, 1E-048, 1E-047, 1E-046, 1E-045, 1E-044, 1E-043, 1E-042, 1E-041, 1E-040, 1E-039, 1E-038, 1E-037, 1E-036, 1E-035, 1E-034, 1E-033, 1E-032, 1E-031, 1E-030, 1E-029, 1E-028, 1E-027, 1E-026, 1E-025, 1E-024, 1E-023, 1E-022, 1E-021, 1E-020, 1E-019, 1E-018, 1E-017, 1E-016, 1E-015, 1E-014, 1E-013, 1E-012, 1E-011, 1E-010, 1E-009, 1E-008, 1E-007, 1E-006, 1E-005, 1E-004, 1E-003, 1E-002, 1E-001, 1E+000, 1E+001, 1E+002, 1E+003, 1E+004, 1E+005, 1E+006, 1E+007, 1E+008, 1E+009, 1E+010, 1E+011, 1E+012, 1E+013, 1E+014, 1E+015, 1E+016, 1E+017, 1E+018, 1E+019, 1E+020, 1E+021, 1E+022, 1E+023, 1E+024, 1E+025, 1E+026, 1E+027, 1E+028, 1E+029, 1E+030, 1E+031, 1E+032, 1E+033, 1E+034, 1E+035, 1E+036, 1E+037, 1E+038, 1E+039, 1E+040, 1E+041, 1E+042, 1E+043, 1E+044, 1E+045, 1E+046, 1E+047, 1E+048, 1E+049, 1E+050, 1E+051, 1E+052, 1E+053, 1E+054, 1E+055, 1E+056, 1E+057, 1E+058, 1E+059, 1E+060, 1E+061, 1E+062, 1E+063, 1E+064, 1E+065, 1E+066, 1E+067, 1E+068, 1E+069, 1E+070, 1E+071, 1E+072, 1E+073, 1E+074, 1E+075, 1E+076, 1E+077, 1E+078, 1E+079, 1E+080, 1E+081, 1E+082, 1E+083, 1E+084, 1E+085, 1E+086, 1E+087, 1E+088, 1E+089, 1E+090, 1E+091, 1E+092, 1E+093, 1E+094, 1E+095, 1E+096, 1E+097, 1E+098, 1E+099, 1E+100, 1E+101, 1E+102, 1E+103, 1E+104, 1E+105, 1E+106, 1E+107, 1E+108, 1E+109, 1E+110, 1E+111, 1E+112, 1E+113, 1E+114, 1E+115, 1E+116, 1E+117, 1E+118, 1E+119, 1E+120, 1E+121, 1E+122, 1E+123, 1E+124, 1E+125, 1E+126, 1E+127, 1E+128, 1E+129, 1E+130, 1E+131, 1E+132, 1E+133, 1E+134, 1E+135, 1E+136, 1E+137, 1E+138, 1E+139, 1E+140, 1E+141, 1E+142, 1E+143, 1E+144, 1E+145, 1E+146, 1E+147, 1E+148, 1E+149, 1E+150, 1E+151, 1E+152, 1E+153, 1E+154, 1E+155, 1E+156, 1E+157, 1E+158, 1E+159, 1E+160, 1E+161, 1E+162, 1E+163, 1E+164, 1E+165, 1E+166, 1E+167, 1E+168, 1E+169, 1E+170, 1E+171, 1E+172, 1E+173, 1E+174, 1E+175, 1E+176, 1E+177, 1E+178, 1E+179, 1E+180, 1E+181, 1E+182, 1E+183, 1E+184, 1E+185, 1E+186, 1E+187, 1E+188, 1E+189, 1E+190, 1E+191, 1E+192, 1E+193, 1E+194, 1E+195, 1E+196, 1E+197, 1E+198, 1E+199, 1E+200, 1E+201, 1E+202, 1E+203, 1E+204, 1E+205, 1E+206, 1E+207, 1E+208, 1E+209, 1E+210, 1E+211, 1E+212, 1E+213, 1E+214, 1E+215, 1E+216, 1E+217, 1E+218, 1E+219, 1E+220, 1E+221, 1E+222, 1E+223, 1E+224, 1E+225, 1E+226, 1E+227, 1E+228, 1E+229, 1E+230, 1E+231, 1E+232, 1E+233, 1E+234, 1E+235, 1E+236, 1E+237, 1E+238, 1E+239, 1E+240, 1E+241, 1E+242, 1E+243, 1E+244, 1E+245, 1E+246, 1E+247, 1E+248, 1E+249, 1E+250, 1E+251, 1E+252, 1E+253, 1E+254, 1E+255, 1E+256, 1E+257, 1E+258, 1E+259, 1E+260, 1E+261, 1E+262, 1E+263, 1E+264, 1E+265, 1E+266, 1E+267, 1E+268, 1E+269, 1E+270, 1E+271, 1E+272, 1E+273, 1E+274, 1E+275, 1E+276, 1E+277, 1E+278, 1E+279, 1E+280, 1E+281, 1E+282, 1E+283, 1E+284, 1E+285, 1E+286, 1E+287, 1E+288, 1E+289, 1E+290, 1E+291, 1E+292, 1E+293, 1E+294, 1E+295, 1E+296, 1E+297, 1E+298, 1E+299, 1E+300, 1E+301, 1E+302, 1E+303, 1E+304, 1E+305, 1E+306, 1E+307, 1E+308 };
    long long b = 0, e1 = 0, e2 = 0;
    bool is_exp = false;
    do
    {
        bool negate = s != end && *s == '-';
        if (s != end && (*s == '-' || *s == '+')) { ++s; }
        bool decimal = false;
        long long &r = is_exp ? e2 : b;
        while (s != end && (*s == '.' || '0' <= *s && *s <= '9'))
        {
            if (*s != '.')
            {
                e1 -= decimal;
                char const digit = *s - '0';
                if (static_cast<unsigned long long>(r) < static_cast<unsigned long long>(r) * 10 + static_cast<unsigned char>(digit))
                {
                    r *= 10;
                    r += digit;
                }
            }
            else { decimal = true; }
            ++s;
        }
        r = negate ? -r : +r;
    } while ((is_exp = !is_exp, is_exp) && s != end && ((*s | ('e' ^ 'E')) == 'e') && (++s, is_exp));
    double const result = b * pow10[323 + (e1 + e2)];
    return result;
}