Optimize QScriptEngine repeated action

2019-09-07 09:40发布

问题:

I'm trying to optimize QScriptEngine operations in one of my functions.

The function is named executeCustomJSOperation and it executes the same JS code in multiple files. However each file needs to change a global variable named $xmlData. The function basicaly loads a XML file to memory using the $xmlData variable and then always apply the same javascript code (jsString) to edit this XML file using javascript. In the end the $xmlData variable is updated with the edited XML again.

I've got a 2.5 speedup using only an OpenMP parallel for over the for loop that processes each XML file. But now I don't know how to proceed to improve this function speed further.

The code is the following:

// 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
    }
}

Do you think it is possible to optimize further this code? If you have any doubt please ask.

回答1:

Why are you creating / initializing a separate QScriptEngine for each iteration? I'd suggest moving everything up to your line

engine.evaluate(jsxmlString);

to outside the for()-loop.

True, this will make things more difficult WRT threading. Essentially you'd have to set up n worker threads, and create one script engine per thread (not per file). For starters a simple single threaded version should give you a first idea of what speedup to expect, and if that is worth the trouble.

Of course, if your JS code really is single use, only, QScriptProgram is your only hope of optimization. Again, you'd set up a limited number of worker threads, each with its own QScriptProgram (and one QScriptEngine per iteration, as in your current code).



回答2:

You can construct a QScriptProgram, put all JS code in it and evaluate it using QScriptEngine::evaluate. It could speed up executing because parsing JS code will be done only once. However, QScriptProgram is not documented as reentrant or thread-safe, so you can't be sure that it will work correctly in multiple threads even if each thread uses its own QScriptProgram object.