Оптимизировать повторяющееся действие QScriptEngine

Я пытаюсь оптимизировать операции QScriptEngine в одной из своих функций.

Функция называется executeCustomJSOperation и выполняет один и тот же код JS в нескольких файлах. Однако в каждом файле необходимо изменить глобальную переменную с именем $xmlData. Функция загружает XML-файл в память с помощью переменной $xmlData, а затем всегда применяет один и тот же код javascript (jsString) для редактирования этого XML-файла с помощью javascript. В конце переменная $xmlData снова обновляется отредактированным XML.

У меня есть ускорение 2.5, использующее только OpenMP parallel for поверх цикла for, который обрабатывает каждый файл XML. Но теперь я не знаю, как поступить, чтобы улучшить скорость этой функции.

Код следующий:

// allows user to echo js variables to check them in terminal using cout
QScriptValue echo(QScriptContext *context, QScriptEngine *engine)
{
    std::cout << context->argument(0).toString().toUtf8().constData() << std::endl; 
    return "";
}

void executeCustomJSOperation(const QString &jsString, const QStringList &filesToProcess){  
    QString rexmlString, jsxmlString;
    QFile rexmlfile(":/resources/libs/rexml.js"); // load javascript libraries as strings to memory
    QFile jsxmlfile(":/resources/libs/jsxml.js");

    rexmlfile.open(QFile::ReadOnly | QFile::Text);
    jsxmlfile.open(QFile::ReadOnly | QFile::Text);

    rexmlString=QTextStream(&rexmlfile).readAll();
    jsxmlString=QTextStream(&jsxmlfile).readAll();

    // Process all XmlFiles
#pragma omp parallel for // 2.5 speedup in my pc
    for(int i=0; i<filesToProcess.size(); i++){

        QString currXmlFileString;

        QScriptEngine engine;
        QScriptValue engineResult;

        // Add echo function so user can debug the code
        QScriptValue echoFunction = engine.newFunction(echo);
        engine.globalObject().setProperty("echo", echoFunction);

        engine.evaluate(rexmlString); // load js libraries in js engine
        engine.evaluate(jsxmlString);

        QFile currXmlFile(filesToProcess[i]);

        currXmlFileString=QTextStream(&currXmlFile).readAll();

        currXmlFile.close(); // close reading

        engine.globalObject().setProperty("$xmlData",currXmlFileString);

        engine.evaluate("main(); function main() {"+jsString+"}"); // main function allows to use return to exit prematurely from user code

        engineResult=engine.globalObject().property("$xmlData");

        QTextStream(&currXmlFile) << engineResult.toString(); // retreive the modified xml by javascript and save it to the file
    }
}

Как вы думаете, можно ли еще оптимизировать этот код? Если у вас есть какие-либо сомнения, пожалуйста, спросите.


person RandomGuy    schedule 15.02.2014    source источник


Ответы (2)


Почему вы создаете/инициализируете отдельный QScriptEngine для каждой итерации? Я бы предложил перенести все на вашу линию

engine.evaluate(jsxmlString);

за пределами for()-loop.

Правда, это усложнит работу с потоками WRT. По сути, вам нужно настроить n рабочих потоков и создать по одному обработчику сценариев для каждого потока (а не для каждого файла). Для начала простая однопоточная версия должна дать вам первое представление о том, какое ускорение ожидать и стоит ли оно того.

Конечно, если ваш JS-код на самом деле одноразовый, QScriptProgram — ваша единственная надежда на оптимизацию. Опять же, вы бы установили ограниченное количество рабочих потоков, каждый со своим собственным QScriptProgram (и одним QScriptEngine на итерацию, как в вашем текущем коде).

person Tfry    schedule 03.11.2014
comment
Спасибо! Я давно не обновлял вопрос с изменениями, которые я внес со временем. Я написал точно так, как вы предложили, я создаю только один QScriptEngine для каждого потока и удаляю избыточный код из цикла. Я не делал измерений, но я знаю, что быстрее. Я попробовал совет QScriptProgram, но не заметил каких-либо значительных изменений в производительности, я выделяю QScriptPrograms в векторе в куче, где каждая позиция используется каждым потоком. Я также пробовал новый QJSEngine, который, как мне кажется, использует V8 JSengine, но результаты у меня гораздо медленнее. :| - person RandomGuy; 10.11.2014

Вы можете создать QScriptProgram, поместить в него весь код JS и оценить его, используя QScriptEngine::evaluate. Это может ускорить выполнение, потому что синтаксический анализ кода JS будет выполняться только один раз. Однако QScriptProgram не задокументирован как реентерабельный или потокобезопасный, поэтому нельзя быть уверенным, что он будет корректно работать в нескольких потоках, даже если каждый поток использует свой собственный объект QScriptProgram.

person Pavel Strakhov    schedule 15.02.2014
comment
Привет, спасибо за подсказку. Я пробовал, но не смог улучшить производительность (процессора). Кроме того, как вы сказали, QScriptProgram не является потокобезопасным, поэтому я не могу просто использовать его с потоками, если только я не использую один для каждого потока, который анализирует js после его создания. Я также пытался использовать одни и те же объекты (включая qengine и qscriptprogram) для всех потоков, и для этого я пробовал некоторые методы синхронизации, такие как omp Critical, qmutex и EnterCriticalSection, но они слишком сильно замедляют работу программы. Самая быстрая реализация до сих пор остается той, которую я опубликовал, где каждый поток имеет отдельные механизмы/переменные. - person RandomGuy; 16.02.2014
comment
Попробуйте создать один QScriptProgram из кода JS, затем создайте несколько его копий (что должно быть дешево) и распределите их по всем потокам. - person Pavel Strakhov; 16.02.2014
comment
К сожалению, эта опция не работает и с потоками. Я получаю ошибку сегментации, когда использую этот метод. Кажется, что конструктор QScriptProgram::QScriptProgram(const QScriptProgram & other) не делает глубокую копию QScriptProgram. Вы можете проверить реализованный код здесь. Обратите внимание, что этот код включает в себя полный код, а не только упрощенный, который я разместил здесь (включая, например, проверку ошибок). - person RandomGuy; 16.02.2014