From 75cffdf92c691b428eb58c67c86baa32c938f6a7 Mon Sep 17 00:00:00 2001 From: Arseny Kapoulkine <arseny.kapoulkine@gmail.com> Date: Mon, 5 Feb 2018 16:07:39 -0800 Subject: [PATCH] Preprocessor: Use std::string instead of std::stringstream std::stringstream has a measurable overhead for preprocessing - it appears that operator<< does a tiny bit of extra work for appending chars/strings and also can't be inlined in most cases on VS2015; additionally, std::endl triggers a stream flush which also adds up. Replacing this with std::string buffer gets the preprocessing time down from 180ms to 135ms in one case, making it 1.33x faster. Note that integer-to-string conversion is using std::to_string; in theory this could be slower than sprintf or manual conversion, but I haven't found these cases to affect preprocessing time in practice (std::to_string would always use the short string buffer for line/version numbers, and the number of calls is not too significant). --- glslang/MachineIndependent/ShaderLang.cpp | 65 +++++++++++++---------- 1 file changed, 37 insertions(+), 28 deletions(-) diff --git a/glslang/MachineIndependent/ShaderLang.cpp b/glslang/MachineIndependent/ShaderLang.cpp index c92d02391..21004fede 100644 --- a/glslang/MachineIndependent/ShaderLang.cpp +++ b/glslang/MachineIndependent/ShaderLang.cpp @@ -912,7 +912,7 @@ bool ProcessDeferred( class SourceLineSynchronizer { public: SourceLineSynchronizer(const std::function<int()>& lastSourceIndex, - std::stringstream* output) + std::string* output) : getLastSourceIndex(lastSourceIndex), output(output), lastSource(-1), lastLine(0) {} // SourceLineSynchronizer(const SourceLineSynchronizer&) = delete; // SourceLineSynchronizer& operator=(const SourceLineSynchronizer&) = delete; @@ -927,7 +927,7 @@ public: // used. We also need to output a newline to separate the output // from the previous source string (if there is one). if (lastSource != -1 || lastLine != 0) - *output << std::endl; + *output += '\n'; lastSource = getLastSourceIndex(); lastLine = -1; return true; @@ -942,7 +942,7 @@ public: syncToMostRecentString(); const bool newLineStarted = lastLine < tokenLine; for (; lastLine < tokenLine; ++lastLine) { - if (lastLine > 0) *output << std::endl; + if (lastLine > 0) *output += '\n'; } return newLineStarted; } @@ -956,8 +956,8 @@ private: // A function for getting the index of the last valid source string we've // read tokens from. const std::function<int()> getLastSourceIndex; - // output stream for newlines. - std::stringstream* output; + // output string for newlines. + std::string* output; // lastSource is the source string index (starting from 0) of the last token // processed. It is tracked in order for newlines to be inserted when a new // source string starts. -1 means we haven't started processing any source @@ -988,27 +988,33 @@ struct DoPreprocessing { parseContext.setScanner(&input); ppContext.setInput(input, versionWillBeError); - std::stringstream outputStream; + std::string outputBuffer; SourceLineSynchronizer lineSync( - std::bind(&TInputScanner::getLastValidSourceIndex, &input), &outputStream); + std::bind(&TInputScanner::getLastValidSourceIndex, &input), &outputBuffer); - parseContext.setExtensionCallback([&lineSync, &outputStream]( + parseContext.setExtensionCallback([&lineSync, &outputBuffer]( int line, const char* extension, const char* behavior) { lineSync.syncToLine(line); - outputStream << "#extension " << extension << " : " << behavior; + outputBuffer += "#extension "; + outputBuffer += extension; + outputBuffer += " : "; + outputBuffer += behavior; }); - parseContext.setLineCallback([&lineSync, &outputStream, &parseContext]( + parseContext.setLineCallback([&lineSync, &outputBuffer, &parseContext]( int curLineNum, int newLineNum, bool hasSource, int sourceNum, const char* sourceName) { // SourceNum is the number of the source-string that is being parsed. lineSync.syncToLine(curLineNum); - outputStream << "#line " << newLineNum; + outputBuffer += "#line "; + outputBuffer += std::to_string(newLineNum); if (hasSource) { - outputStream << " "; + outputBuffer += ' '; if (sourceName != nullptr) { - outputStream << "\"" << sourceName << "\""; + outputBuffer += '\"'; + outputBuffer += sourceName; + outputBuffer += '\"'; } else { - outputStream << sourceNum; + outputBuffer += std::to_string(sourceNum); } } if (parseContext.lineDirectiveShouldSetNextLine()) { @@ -1016,33 +1022,36 @@ struct DoPreprocessing { // directive. So the new line number for the current line is newLineNum -= 1; } - outputStream << std::endl; + outputBuffer += '\n'; // And we are at the next line of the #line directive now. lineSync.setLineNum(newLineNum + 1); }); parseContext.setVersionCallback( - [&lineSync, &outputStream](int line, int version, const char* str) { + [&lineSync, &outputBuffer](int line, int version, const char* str) { lineSync.syncToLine(line); - outputStream << "#version " << version; + outputBuffer += "#version "; + outputBuffer += std::to_string(version); if (str) { - outputStream << " " << str; + outputBuffer += ' '; + outputBuffer += str; } }); - parseContext.setPragmaCallback([&lineSync, &outputStream]( + parseContext.setPragmaCallback([&lineSync, &outputBuffer]( int line, const glslang::TVector<glslang::TString>& ops) { lineSync.syncToLine(line); - outputStream << "#pragma "; + outputBuffer += "#pragma "; for(size_t i = 0; i < ops.size(); ++i) { - outputStream << ops[i]; + outputBuffer += ops[i].c_str(); } }); - parseContext.setErrorCallback([&lineSync, &outputStream]( + parseContext.setErrorCallback([&lineSync, &outputBuffer]( int line, const char* errorMessage) { lineSync.syncToLine(line); - outputStream << "#error " << errorMessage; + outputBuffer += "#error "; + outputBuffer += errorMessage; }); int lastToken = EndOfInput; // lastToken records the last token processed. @@ -1058,7 +1067,7 @@ struct DoPreprocessing { // Don't emit whitespace onto empty lines. // Copy any whitespace characters at the start of a line // from the input to the output. - outputStream << std::string(ppToken.loc.column - 1, ' '); + outputBuffer += std::string(ppToken.loc.column - 1, ' '); } // Output a space in between tokens, but not at the start of a line, @@ -1068,13 +1077,13 @@ struct DoPreprocessing { (unNeededSpaceTokens.find((char)token) == std::string::npos) && (unNeededSpaceTokens.find((char)lastToken) == std::string::npos) && (noSpaceBeforeTokens.find((char)token) == std::string::npos)) { - outputStream << " "; + outputBuffer += ' '; } lastToken = token; - outputStream << ppToken.name; + outputBuffer += ppToken.name; } while (true); - outputStream << std::endl; - *outputString = outputStream.str(); + outputBuffer += '\n'; + *outputString = std::move(outputBuffer); bool success = true; if (parseContext.getNumErrors() > 0) { -- GitLab