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).
This commit is contained in:
parent
82ead04c39
commit
75cffdf92c
@ -912,7 +912,7 @@ bool ProcessDeferred(
|
|||||||
class SourceLineSynchronizer {
|
class SourceLineSynchronizer {
|
||||||
public:
|
public:
|
||||||
SourceLineSynchronizer(const std::function<int()>& lastSourceIndex,
|
SourceLineSynchronizer(const std::function<int()>& lastSourceIndex,
|
||||||
std::stringstream* output)
|
std::string* output)
|
||||||
: getLastSourceIndex(lastSourceIndex), output(output), lastSource(-1), lastLine(0) {}
|
: getLastSourceIndex(lastSourceIndex), output(output), lastSource(-1), lastLine(0) {}
|
||||||
// SourceLineSynchronizer(const SourceLineSynchronizer&) = delete;
|
// SourceLineSynchronizer(const SourceLineSynchronizer&) = delete;
|
||||||
// SourceLineSynchronizer& operator=(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
|
// used. We also need to output a newline to separate the output
|
||||||
// from the previous source string (if there is one).
|
// from the previous source string (if there is one).
|
||||||
if (lastSource != -1 || lastLine != 0)
|
if (lastSource != -1 || lastLine != 0)
|
||||||
*output << std::endl;
|
*output += '\n';
|
||||||
lastSource = getLastSourceIndex();
|
lastSource = getLastSourceIndex();
|
||||||
lastLine = -1;
|
lastLine = -1;
|
||||||
return true;
|
return true;
|
||||||
@ -942,7 +942,7 @@ public:
|
|||||||
syncToMostRecentString();
|
syncToMostRecentString();
|
||||||
const bool newLineStarted = lastLine < tokenLine;
|
const bool newLineStarted = lastLine < tokenLine;
|
||||||
for (; lastLine < tokenLine; ++lastLine) {
|
for (; lastLine < tokenLine; ++lastLine) {
|
||||||
if (lastLine > 0) *output << std::endl;
|
if (lastLine > 0) *output += '\n';
|
||||||
}
|
}
|
||||||
return newLineStarted;
|
return newLineStarted;
|
||||||
}
|
}
|
||||||
@ -956,8 +956,8 @@ private:
|
|||||||
// A function for getting the index of the last valid source string we've
|
// A function for getting the index of the last valid source string we've
|
||||||
// read tokens from.
|
// read tokens from.
|
||||||
const std::function<int()> getLastSourceIndex;
|
const std::function<int()> getLastSourceIndex;
|
||||||
// output stream for newlines.
|
// output string for newlines.
|
||||||
std::stringstream* output;
|
std::string* output;
|
||||||
// lastSource is the source string index (starting from 0) of the last token
|
// 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
|
// 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
|
// source string starts. -1 means we haven't started processing any source
|
||||||
@ -988,27 +988,33 @@ struct DoPreprocessing {
|
|||||||
parseContext.setScanner(&input);
|
parseContext.setScanner(&input);
|
||||||
ppContext.setInput(input, versionWillBeError);
|
ppContext.setInput(input, versionWillBeError);
|
||||||
|
|
||||||
std::stringstream outputStream;
|
std::string outputBuffer;
|
||||||
SourceLineSynchronizer lineSync(
|
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) {
|
int line, const char* extension, const char* behavior) {
|
||||||
lineSync.syncToLine(line);
|
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) {
|
int curLineNum, int newLineNum, bool hasSource, int sourceNum, const char* sourceName) {
|
||||||
// SourceNum is the number of the source-string that is being parsed.
|
// SourceNum is the number of the source-string that is being parsed.
|
||||||
lineSync.syncToLine(curLineNum);
|
lineSync.syncToLine(curLineNum);
|
||||||
outputStream << "#line " << newLineNum;
|
outputBuffer += "#line ";
|
||||||
|
outputBuffer += std::to_string(newLineNum);
|
||||||
if (hasSource) {
|
if (hasSource) {
|
||||||
outputStream << " ";
|
outputBuffer += ' ';
|
||||||
if (sourceName != nullptr) {
|
if (sourceName != nullptr) {
|
||||||
outputStream << "\"" << sourceName << "\"";
|
outputBuffer += '\"';
|
||||||
|
outputBuffer += sourceName;
|
||||||
|
outputBuffer += '\"';
|
||||||
} else {
|
} else {
|
||||||
outputStream << sourceNum;
|
outputBuffer += std::to_string(sourceNum);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (parseContext.lineDirectiveShouldSetNextLine()) {
|
if (parseContext.lineDirectiveShouldSetNextLine()) {
|
||||||
@ -1016,33 +1022,36 @@ struct DoPreprocessing {
|
|||||||
// directive. So the new line number for the current line is
|
// directive. So the new line number for the current line is
|
||||||
newLineNum -= 1;
|
newLineNum -= 1;
|
||||||
}
|
}
|
||||||
outputStream << std::endl;
|
outputBuffer += '\n';
|
||||||
// And we are at the next line of the #line directive now.
|
// And we are at the next line of the #line directive now.
|
||||||
lineSync.setLineNum(newLineNum + 1);
|
lineSync.setLineNum(newLineNum + 1);
|
||||||
});
|
});
|
||||||
|
|
||||||
parseContext.setVersionCallback(
|
parseContext.setVersionCallback(
|
||||||
[&lineSync, &outputStream](int line, int version, const char* str) {
|
[&lineSync, &outputBuffer](int line, int version, const char* str) {
|
||||||
lineSync.syncToLine(line);
|
lineSync.syncToLine(line);
|
||||||
outputStream << "#version " << version;
|
outputBuffer += "#version ";
|
||||||
|
outputBuffer += std::to_string(version);
|
||||||
if (str) {
|
if (str) {
|
||||||
outputStream << " " << str;
|
outputBuffer += ' ';
|
||||||
|
outputBuffer += str;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
parseContext.setPragmaCallback([&lineSync, &outputStream](
|
parseContext.setPragmaCallback([&lineSync, &outputBuffer](
|
||||||
int line, const glslang::TVector<glslang::TString>& ops) {
|
int line, const glslang::TVector<glslang::TString>& ops) {
|
||||||
lineSync.syncToLine(line);
|
lineSync.syncToLine(line);
|
||||||
outputStream << "#pragma ";
|
outputBuffer += "#pragma ";
|
||||||
for(size_t i = 0; i < ops.size(); ++i) {
|
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) {
|
int line, const char* errorMessage) {
|
||||||
lineSync.syncToLine(line);
|
lineSync.syncToLine(line);
|
||||||
outputStream << "#error " << errorMessage;
|
outputBuffer += "#error ";
|
||||||
|
outputBuffer += errorMessage;
|
||||||
});
|
});
|
||||||
|
|
||||||
int lastToken = EndOfInput; // lastToken records the last token processed.
|
int lastToken = EndOfInput; // lastToken records the last token processed.
|
||||||
@ -1058,7 +1067,7 @@ struct DoPreprocessing {
|
|||||||
// Don't emit whitespace onto empty lines.
|
// Don't emit whitespace onto empty lines.
|
||||||
// Copy any whitespace characters at the start of a line
|
// Copy any whitespace characters at the start of a line
|
||||||
// from the input to the output.
|
// 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,
|
// 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)token) == std::string::npos) &&
|
||||||
(unNeededSpaceTokens.find((char)lastToken) == std::string::npos) &&
|
(unNeededSpaceTokens.find((char)lastToken) == std::string::npos) &&
|
||||||
(noSpaceBeforeTokens.find((char)token) == std::string::npos)) {
|
(noSpaceBeforeTokens.find((char)token) == std::string::npos)) {
|
||||||
outputStream << " ";
|
outputBuffer += ' ';
|
||||||
}
|
}
|
||||||
lastToken = token;
|
lastToken = token;
|
||||||
outputStream << ppToken.name;
|
outputBuffer += ppToken.name;
|
||||||
} while (true);
|
} while (true);
|
||||||
outputStream << std::endl;
|
outputBuffer += '\n';
|
||||||
*outputString = outputStream.str();
|
*outputString = std::move(outputBuffer);
|
||||||
|
|
||||||
bool success = true;
|
bool success = true;
|
||||||
if (parseContext.getNumErrors() > 0) {
|
if (parseContext.getNumErrors() > 0) {
|
||||||
|
Loading…
x
Reference in New Issue
Block a user