Improve parser performance by reducing tracing overhead (#10533)

Passing structures larger than the register size is very expensive
due to Microsoft's x64 calling convention. We could reduce the
overhead by passing the string-view by reference, but this forces us
to allocate the parameters as static string-views on the data
segment of our binary. I've found that passing them as classic
C-strings is more ergonomic instead and fits the need for
high performance in this particular code.
This improves performance for VT-heavy output by 15-20%.

## PR Checklist
* [x] I work here
* [x] Tests added/passed
This commit is contained in:
Leonard Hecker 2021-06-30 02:28:40 +02:00 committed by GitHub
parent ab5a8d701d
commit ee3259847a
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
2 changed files with 24 additions and 19 deletions

View file

@ -10,25 +10,20 @@ using namespace Microsoft::Console::VirtualTerminal;
#pragma warning(disable : 26447) // The function is declared 'noexcept' but calls function '_tlgWrapBinary<wchar_t>()' which may throw exceptions
#pragma warning(disable : 26477) // Use 'nullptr' rather than 0 or NULL
ParserTracing::ParserTracing() noexcept
{
ClearSequenceTrace();
}
void ParserTracing::TraceStateChange(const std::wstring_view name) const noexcept
void ParserTracing::TraceStateChange(_In_z_ const wchar_t* name) const noexcept
{
TraceLoggingWrite(g_hConsoleVirtTermParserEventTraceProvider,
"StateMachine_EnterState",
TraceLoggingCountedWideString(name.data(), gsl::narrow_cast<ULONG>(name.size())),
TraceLoggingWideString(name),
TraceLoggingLevel(WINEVENT_LEVEL_VERBOSE),
TraceLoggingKeyword(TIL_KEYWORD_TRACE));
}
void ParserTracing::TraceOnAction(const std::wstring_view name) const noexcept
void ParserTracing::TraceOnAction(_In_z_ const wchar_t* name) const noexcept
{
TraceLoggingWrite(g_hConsoleVirtTermParserEventTraceProvider,
"StateMachine_Action",
TraceLoggingCountedWideString(name.data(), gsl::narrow_cast<ULONG>(name.size())),
TraceLoggingWideString(name),
TraceLoggingLevel(WINEVENT_LEVEL_VERBOSE),
TraceLoggingKeyword(TIL_KEYWORD_TRACE));
}
@ -55,11 +50,11 @@ void ParserTracing::TraceOnExecuteFromEscape(const wchar_t wch) const noexcept
TraceLoggingKeyword(TIL_KEYWORD_TRACE));
}
void ParserTracing::TraceOnEvent(const std::wstring_view name) const noexcept
void ParserTracing::TraceOnEvent(_In_z_ const wchar_t* name) const noexcept
{
TraceLoggingWrite(g_hConsoleVirtTermParserEventTraceProvider,
"StateMachine_Event",
TraceLoggingCountedWideString(name.data(), gsl::narrow_cast<ULONG>(name.size())),
TraceLoggingWideString(name),
TraceLoggingLevel(WINEVENT_LEVEL_VERBOSE),
TraceLoggingKeyword(TIL_KEYWORD_TRACE));
}
@ -67,12 +62,11 @@ void ParserTracing::TraceOnEvent(const std::wstring_view name) const noexcept
void ParserTracing::TraceCharInput(const wchar_t wch)
{
AddSequenceTrace(wch);
const auto sch = gsl::narrow_cast<INT16>(wch);
TraceLoggingWrite(g_hConsoleVirtTermParserEventTraceProvider,
"StateMachine_NewChar",
TraceLoggingWChar(wch),
TraceLoggingHexInt16(sch),
TraceLoggingHexInt16(gsl::narrow_cast<INT16>(wch)),
TraceLoggingLevel(WINEVENT_LEVEL_VERBOSE),
TraceLoggingKeyword(TIL_KEYWORD_TRACE));
}
@ -114,7 +108,7 @@ void ParserTracing::ClearSequenceTrace() noexcept
}
// NOTE: I'm expecting this to not be null terminated
void ParserTracing::DispatchPrintRunTrace(const std::wstring_view string) const
void ParserTracing::DispatchPrintRunTrace(const std::wstring_view& string) const
{
if (string.size() == 1)
{

View file

@ -21,19 +21,30 @@ namespace Microsoft::Console::VirtualTerminal
class ParserTracing sealed
{
public:
ParserTracing() noexcept;
// NOTE: This code uses
// (_In_z_ const wchar_t* name)
// as arguments instead of the more modern std::wstring_view
// for performance reasons.
//
// Passing structures larger than the register size is very expensive
// due to Microsoft's x64 calling convention. We could reduce the
// overhead by passing the string-view by reference, but this forces us
// to allocate the parameters as static string-views on the data
// segment of our binary. I've found that passing them as classic
// C-strings is more ergonomic instead and fits the need for
// high performance in this particular code.
void TraceStateChange(const std::wstring_view name) const noexcept;
void TraceOnAction(const std::wstring_view name) const noexcept;
void TraceStateChange(_In_z_ const wchar_t* name) const noexcept;
void TraceOnAction(_In_z_ const wchar_t* name) const noexcept;
void TraceOnExecute(const wchar_t wch) const noexcept;
void TraceOnExecuteFromEscape(const wchar_t wch) const noexcept;
void TraceOnEvent(const std::wstring_view name) const noexcept;
void TraceOnEvent(_In_z_ const wchar_t* name) const noexcept;
void TraceCharInput(const wchar_t wch);
void AddSequenceTrace(const wchar_t wch);
void DispatchSequenceTrace(const bool fSuccess) noexcept;
void ClearSequenceTrace() noexcept;
void DispatchPrintRunTrace(const std::wstring_view string) const;
void DispatchPrintRunTrace(const std::wstring_view& string) const;
private:
std::wstring _sequenceTrace;