Repository: microsoft/terminal
File: src/renderer/base/renderer.cpp (C++)

Code before:
            cols = 0;

            // Hold onto the start of this run iterator and the target location where we started
            // in case we need to do some special work to paint the line drawing characters.
            const auto currentRunItStart = it;
            const auto currentRunTargetStart = screenPoint;

            // Ensure that our cluster vector is clear.
            _clusterBuffer.clear();

            // Reset our flag to know when we're in the special circumstance
            // of attempting to draw only the right-half of a two-column character
            // as the first item in our run.
            bool trimLeft = false;

            // Run contains wide character (>1 columns)
            bool containsWideCharacter = false;

            // This inner loop will accumulate clusters until the color changes.
            // When the color changes, it will save the new color off and break.
            // We also accumulate clusters according to regex patterns
            do
            {
                COORD thisPoint{ screenPoint.X + gsl::narrow<SHORT>(cols), screenPoint.Y };
                const auto thisPointPatterns = _pData->GetPatternId(thisPoint);
                if (color != it->TextAttr() || (thisPointPatterns.size() > 0 && patternIds != thisPointPatterns))
                {
                    auto newAttr{ it->TextAttr() };
                    // foreground doesn't matter for runs of spaces (!)
                    // if we trick it . . . we call Paint far fewer times for cmatrix
                    if (!_IsAllSpaces(it->Chars()) || !newAttr.HasIdenticalVisualRepresentationForBlankSpace(color, globalInvert) || patternIds != thisPointPatterns)
                    {
                        color = newAttr;
                        patternIds = thisPointPatterns;
                        break; // vend this run
                    }
                }

                // Walk through the text data and turn it into rendering clusters.
                // Keep the columnCount as we go to improve performance over digging it out of the vector at the end.
                size_t columnCount = 0;

                // If we're on the first cluster to be added and it's marked as "trailing"
                // (a.k.a. the right half of a two column character), then we need some special handling.
                if (_clusterBuffer.empty() && it->DbcsAttr().IsTrailing())
                {
                    // Move left to the one so the whole character can be struck correctly.
                    --screenPoint.X;
                    // And tell the next function to trim off the left half of it.
                    trimLeft = true;
                    // And add one to the number of columns we expect it to take as we insert it.

Review comment (performance):
This change is crucial, and reduces 3% of the CPU time.

How could comparison between two *empty* vectors be so expensive 😓  I don't really get it.

Code after:
        auto usingSoftFont = s_IsSoftFontChar(it->Chars(), _firstSoftFontChar, _lastSoftFontChar);

        // And hold the point where we should start drawing.
        auto screenPoint = target;

        // This outer loop will continue until we reach the end of the text we are trying to draw.
        while (it)
        {
            // Hold onto the current run color right here for the length of the outer loop.
            // We'll be changing the persistent one as we run through the inner loops to detect
            // when a run changes, but we will still need to know this color at the bottom
            // when we go to draw gridlines for the length of the run.
            const auto currentRunColor = color;

            // Hold onto the current pattern id as well
            const auto currentPatternId = patternIds;

            // Update the drawing brushes with our color and font usage.
            THROW_IF_FAILED(_UpdateDrawingBrushes(pEngine, currentRunColor, usingSoftFont, false));

            // Advance the point by however many columns we've just outputted and reset the accumulator.
            screenPoint.X += gsl::narrow<SHORT>(cols);
            cols = 0;

            // Hold onto the start of this run iterator and the target location where we started
            // in case we need to do some special work to paint the line drawing characters.
            const auto currentRunItStart = it;
            const auto currentRunTargetStart = screenPoint;

            // Ensure that our cluster vector is clear.
            _clusterBuffer.clear();

            // Reset our flag to know when we're in the special circumstance
            // of attempting to draw only the right-half of a two-column character
            // as the first item in our run.
            bool trimLeft = false;

            // Run contains wide character (>1 columns)
            bool containsWideCharacter = false;

            // This inner loop will accumulate clusters until the color changes.
            // When the color changes, it will save the new color off and break.
            // We also accumulate clusters according to regex patterns
            do
            {
                COORD thisPoint{ screenPoint.X + gsl::narrow<SHORT>(cols), screenPoint.Y };
                const auto thisPointPatterns = _pData->GetPatternId(thisPoint);
                const auto thisUsingSoftFont = s_IsSoftFontChar(it->Chars(), _firstSoftFontChar, _lastSoftFontChar);
                const auto changedPatternOrFont = patternIds != thisPointPatterns || usingSoftFont != thisUsingSoftFont;
                if (color != it->TextAttr() || changedPatternOrFont)
                {