C++ Exprtk против Python eval() ⇐ C++
-
Гость
C++ Exprtk против Python eval()
Проблема в следующем.
Текстовый файл содержит миллионы строк арифметических данных, которые требуют быстрой обработки.
Я изучал варианты решения этой проблемы и написал небольшой скрипт, используя хорошую библиотеку exprtk C++.
Код работает и способен оценивать выражения, но работает медленнее, чем я ожидал. Строки арифметических операций могут оказаться довольно длинными, что может усугубить проблему. Ради интереса я сравнил время вычисления с базовой командой Python eval() и был удивлен, что eval() работает в 3-4 раза быстрее, чем exprtk! Вот код C++:
#include #include #include #include #include #include "exprtk.hpp" интервал основной() { typedef exprtk::symbol_tablesymbol_table_t; typedef exprtk::expression выражение_t; typedef exprtk::parser parser_t; typedef exprtk::parser_error::type error_t; // Определение переменных в строках двойной А = 1,1; двойной Б = 2,2; двойной С = 3,3; двойной м3 = 1,0; двойной z3 = 2,0; символ_таблица_т символ_таблица; символ_таблица.add_constants(); символ_таблица.add_variable("А", А); symbol_table.add_variable("B", B); символ_таблица.add_variable("C", C); символ_таблица.add_variable("м3", м3); символ_таблица.add_variable("z3", z3); выражение_выражение_t; выражение.register_symbol_table(symbol_table); parser_t парсер; // Загружаем текстовый файл и перебираем строки std::ifstream fin("test.txt"); std::string file_line; while(std::getline(fin, file_line)) { // текущая строка текста находится в file_line, не включая \n std::stringpression_str = file_line; if(!parser.compile(expression_str, выражение)) { printf("Ошибка: %s\tExpression: %s\n", parser.error().c_str(), выражение_str.c_str()); for(std::size_t i = 0; i < parser.error_count(); ++i) { const error_t error = parser.get_error(i); printf("Ошибка: %02d Позиция: %02d Тип: [%s] Сообщение: %s Выражение: %s\n", static_cast(i), static_cast(error.token.position), exprtk::parser_error::to_str(error.mode).c_str(), error.diagnostic.c_str(), выражение_str.c_str()); } возврат 1; } двойной результат = выражение.значение(); printf("%10.16f\n", результат); } вернуть 0; } Вот код Python:
с open("test.txt", 'r') как h: линииHH = h.readlines() // Применяем понимание списка matt = [eval(x) для x в строкахHH] Текстовый файл имеет очень простой формат: новая строка арифметики следует за другой. Пример первой строки следующий:
-10./(A+B)^4-2.500000000000000*A^3/C^3/(A+B)^4-9.25000000000000*A/C/(A+B)^4- 2.500000000000000*B^3/C^3/(A+B)^4-9.250000000000000*B/C/(A+B)^4-8.*A^2/C^3/(A+B)^4 -8.*B^2/C^3/(A+B)^4-1.750000000000000*A/B/(A+B)^4+2.250000000000000*B^2/C^2/(A+B)^ 4-1.750000000000000/A*B/(A+B)^4-1./A^2*B^2/(A+B)^4-.2500000000000000/A^3*B^3/(A+B )^4-13.*A/C^2/(A+B)^4-13.*B/C^2/(A+B)^4-.2500000000000000*A^3/B^3/( A+B)^4+2.250000000000000*A^2/C^2/(A+B)^4-1.*A^2/B^2/(A+B)^4+62./C/( A+B)^4*z3-11./C/(A+B)^4-13.*A^2*B/C^3/(A+B)^4+3.500000000000000*A^2/B /C/(A+B)^4-13.*A*B^2/C^3/(A+B)^4+3.500000000000000/A*B^2/C/(A+B)^4- 14.*A*B/C^3/(A+B)^4-.5000000000000000/A*B^4/C^3/(A+B)^4-1./A*B^3/C ^2/(A+B)^4-.2500000000000000/A^2*B^4/C^2/(A+B)^4-2./A^2*B^3/C/(A+ B)^4-.2500000000000000/A^3*B^4/C/(A+B)^4-1.*A^3/B/C^3/(A+B)^4-.5000000000000000* A^3/B^2/C^2/(A+B)^4-2,500000000000000*A^2/B/C^2/(A+B)^4-.5000000000000000*A^2/B^2 /C/(A+B)^4-2.*A/B/C/(A+B)^4-1./A*B^3/C^3/(A+B)^4-2.500000000000000 /A*B^2/C^2/(A+B)^4-2./A*B/C/(A+B)^4-.5000000000000000/A^2*B^3/C^2 /(A+B)^4-.5000000000000000/A^2*B^2/C/(A+B)^4-.5000000000000000*A^4/B/C^3/(A+B)^4 -.2500000000000000*A^4/B^2/C^2/(A+B)^4-.2500000000000000*A^4/B^3/C/(A+B)^4-1.*A^ 3/B/C^2/(A+B)^4-2.*A^3/B^2/C/(A+B)^4-18.*A*B/C^2/(A +B)^4+26.*A/C^2/(A+B)^4*z3+26.*B/C^2/(A+B)^4*z3+11.*A/B /C/(A+B)^4*z3+5./A*B^2/C^2/(A+B)^4*z3+11./A*B/C/(A+B) ^4*z3+1/A^2*B^3/C^2/(A+B)^4*z3+5./A^2*B^2/C/(A+B)^4* z3+1/A^3*B^3/C/(A+B)^4*z3+A^3/B^2/C^2/(A+B)^4*z3+A^3/ B^3/C/(A+B)^4*z3+5.*A^2/B/C^2/(A+B)^4*z3+5.*A^2/B^2/ С/(А+В)^4*z3 Должен ли я этому удивляться?
Я был удивлен, прочитав документацию, в которой подчеркивалось, что eval() работает медленно и его, как правило, следует избегать (в основном из-за присущих ему проблем с безопасностью), но в этом конкретном примере он, похоже, работает лучше, чем мой код.
Я не верю, что exprtk является потокобезопасным, поэтому сомневаюсь, что существует возможность многопоточности.
Для ускорения можно использовать алгоритм сортировочной станции и обратную полированную нотацию, но уже после этого сравнения я был удивлен разницей в скорости между C++ и Python. Есть ли очевидная причина такой разницы в скорости?
В exprtk намного больше накладных расходов или мой код просто полный мусор? Наверное последнее...
Изменить
Причина, по которой я выбрал библиотеку exprtk, связана с чтением этого сравнительного исследования математического парсера.
Проблема в следующем.
Текстовый файл содержит миллионы строк арифметических данных, которые требуют быстрой обработки.
Я изучал варианты решения этой проблемы и написал небольшой скрипт, используя хорошую библиотеку exprtk C++.
Код работает и способен оценивать выражения, но работает медленнее, чем я ожидал. Строки арифметических операций могут оказаться довольно длинными, что может усугубить проблему. Ради интереса я сравнил время вычисления с базовой командой Python eval() и был удивлен, что eval() работает в 3-4 раза быстрее, чем exprtk! Вот код C++:
#include #include #include #include #include #include "exprtk.hpp" интервал основной() { typedef exprtk::symbol_tablesymbol_table_t; typedef exprtk::expression выражение_t; typedef exprtk::parser parser_t; typedef exprtk::parser_error::type error_t; // Определение переменных в строках двойной А = 1,1; двойной Б = 2,2; двойной С = 3,3; двойной м3 = 1,0; двойной z3 = 2,0; символ_таблица_т символ_таблица; символ_таблица.add_constants(); символ_таблица.add_variable("А", А); symbol_table.add_variable("B", B); символ_таблица.add_variable("C", C); символ_таблица.add_variable("м3", м3); символ_таблица.add_variable("z3", z3); выражение_выражение_t; выражение.register_symbol_table(symbol_table); parser_t парсер; // Загружаем текстовый файл и перебираем строки std::ifstream fin("test.txt"); std::string file_line; while(std::getline(fin, file_line)) { // текущая строка текста находится в file_line, не включая \n std::stringpression_str = file_line; if(!parser.compile(expression_str, выражение)) { printf("Ошибка: %s\tExpression: %s\n", parser.error().c_str(), выражение_str.c_str()); for(std::size_t i = 0; i < parser.error_count(); ++i) { const error_t error = parser.get_error(i); printf("Ошибка: %02d Позиция: %02d Тип: [%s] Сообщение: %s Выражение: %s\n", static_cast(i), static_cast(error.token.position), exprtk::parser_error::to_str(error.mode).c_str(), error.diagnostic.c_str(), выражение_str.c_str()); } возврат 1; } двойной результат = выражение.значение(); printf("%10.16f\n", результат); } вернуть 0; } Вот код Python:
с open("test.txt", 'r') как h: линииHH = h.readlines() // Применяем понимание списка matt = [eval(x) для x в строкахHH] Текстовый файл имеет очень простой формат: новая строка арифметики следует за другой. Пример первой строки следующий:
-10./(A+B)^4-2.500000000000000*A^3/C^3/(A+B)^4-9.25000000000000*A/C/(A+B)^4- 2.500000000000000*B^3/C^3/(A+B)^4-9.250000000000000*B/C/(A+B)^4-8.*A^2/C^3/(A+B)^4 -8.*B^2/C^3/(A+B)^4-1.750000000000000*A/B/(A+B)^4+2.250000000000000*B^2/C^2/(A+B)^ 4-1.750000000000000/A*B/(A+B)^4-1./A^2*B^2/(A+B)^4-.2500000000000000/A^3*B^3/(A+B )^4-13.*A/C^2/(A+B)^4-13.*B/C^2/(A+B)^4-.2500000000000000*A^3/B^3/( A+B)^4+2.250000000000000*A^2/C^2/(A+B)^4-1.*A^2/B^2/(A+B)^4+62./C/( A+B)^4*z3-11./C/(A+B)^4-13.*A^2*B/C^3/(A+B)^4+3.500000000000000*A^2/B /C/(A+B)^4-13.*A*B^2/C^3/(A+B)^4+3.500000000000000/A*B^2/C/(A+B)^4- 14.*A*B/C^3/(A+B)^4-.5000000000000000/A*B^4/C^3/(A+B)^4-1./A*B^3/C ^2/(A+B)^4-.2500000000000000/A^2*B^4/C^2/(A+B)^4-2./A^2*B^3/C/(A+ B)^4-.2500000000000000/A^3*B^4/C/(A+B)^4-1.*A^3/B/C^3/(A+B)^4-.5000000000000000* A^3/B^2/C^2/(A+B)^4-2,500000000000000*A^2/B/C^2/(A+B)^4-.5000000000000000*A^2/B^2 /C/(A+B)^4-2.*A/B/C/(A+B)^4-1./A*B^3/C^3/(A+B)^4-2.500000000000000 /A*B^2/C^2/(A+B)^4-2./A*B/C/(A+B)^4-.5000000000000000/A^2*B^3/C^2 /(A+B)^4-.5000000000000000/A^2*B^2/C/(A+B)^4-.5000000000000000*A^4/B/C^3/(A+B)^4 -.2500000000000000*A^4/B^2/C^2/(A+B)^4-.2500000000000000*A^4/B^3/C/(A+B)^4-1.*A^ 3/B/C^2/(A+B)^4-2.*A^3/B^2/C/(A+B)^4-18.*A*B/C^2/(A +B)^4+26.*A/C^2/(A+B)^4*z3+26.*B/C^2/(A+B)^4*z3+11.*A/B /C/(A+B)^4*z3+5./A*B^2/C^2/(A+B)^4*z3+11./A*B/C/(A+B) ^4*z3+1/A^2*B^3/C^2/(A+B)^4*z3+5./A^2*B^2/C/(A+B)^4* z3+1/A^3*B^3/C/(A+B)^4*z3+A^3/B^2/C^2/(A+B)^4*z3+A^3/ B^3/C/(A+B)^4*z3+5.*A^2/B/C^2/(A+B)^4*z3+5.*A^2/B^2/ С/(А+В)^4*z3 Должен ли я этому удивляться?
Я был удивлен, прочитав документацию, в которой подчеркивалось, что eval() работает медленно и его, как правило, следует избегать (в основном из-за присущих ему проблем с безопасностью), но в этом конкретном примере он, похоже, работает лучше, чем мой код.
Я не верю, что exprtk является потокобезопасным, поэтому сомневаюсь, что существует возможность многопоточности.
Для ускорения можно использовать алгоритм сортировочной станции и обратную полированную нотацию, но уже после этого сравнения я был удивлен разницей в скорости между C++ и Python. Есть ли очевидная причина такой разницы в скорости?
В exprtk намного больше накладных расходов или мой код просто полный мусор? Наверное последнее...
Изменить
Причина, по которой я выбрал библиотеку exprtk, связана с чтением этого сравнительного исследования математического парсера.
Мобильная версия