Skip to content

Commit

Permalink
Improve parser performance by reducing tracing overhead (#10533)
Browse files Browse the repository at this point in the history
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

(cherry picked from commit ee32598)
  • Loading branch information
lhecker authored and DHowett committed Jul 7, 2021
1 parent cd6ec2d commit cf85905
Show file tree
Hide file tree
Showing 2 changed files with 25 additions and 20 deletions.
22 changes: 8 additions & 14 deletions src/terminal/parser/tracing.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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));
}
Expand All @@ -55,24 +50,23 @@ 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));
}

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));
}
Expand Down Expand Up @@ -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)
{
Expand Down
23 changes: 17 additions & 6 deletions src/terminal/parser/tracing.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -21,19 +21,30 @@ namespace Microsoft::Console::VirtualTerminal
class ParserTracing sealed
{
public:
ParserTracing() noexcept;

void TraceStateChange(const std::wstring_view name) const noexcept;
void TraceOnAction(const std::wstring_view name) const 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(_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;
Expand Down

0 comments on commit cf85905

Please sign in to comment.