File: RegexClassifierBenchmarks.cs
Web Access
Project: src\src\Tools\IdeBenchmarks\IdeBenchmarks.csproj (IdeBenchmarks)
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
 
#nullable disable
 
using System.Collections.Immutable;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using BenchmarkDotNet.Attributes;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.Classification;
using Microsoft.CodeAnalysis.Editor.UnitTests.Workspaces;
using Microsoft.CodeAnalysis.Extensions;
using Microsoft.CodeAnalysis.PooledObjects;
using Microsoft.CodeAnalysis.Shared.Extensions;
using Microsoft.CodeAnalysis.Test.Utilities;
using Microsoft.CodeAnalysis.Text;
 
namespace IdeBenchmarks
{
    public class RegexClassifierBenchmarks
    {
        private readonly UseExportProviderAttribute _useExportProviderAttribute = new UseExportProviderAttribute();
 
        [Params(0, 1000, 10000)]
        public int StringLength { get; set; }
 
        [Params('a', '\\')]
        public char RepeatElement { get; set; }
 
        [IterationSetup]
        public void IterationSetup()
            => _useExportProviderAttribute.Before(null);
 
        [IterationCleanup]
        public void IterationCleanup()
            => _useExportProviderAttribute.After(null);
 
        [Benchmark(Baseline = true, Description = "String literal")]
        public object TestStringLiteral()
        {
            var code = CreateTestInput(isRegularExpression: false, element: RepeatElement, length: StringLength);
            return GetClassificationSpansAsync(code, new TextSpan(0, code.Length), parseOptions: null).Result;
        }
 
        [Benchmark(Description = "Regular expression")]
        public object TestEmptyRegexStringLiteral()
        {
            var code = CreateTestInput(isRegularExpression: true, element: RepeatElement, length: StringLength);
            return GetClassificationSpansAsync(code, new TextSpan(0, code.Length), parseOptions: null).Result;
        }
 
        private static string CreateTestInput(bool isRegularExpression, char element, int length)
        {
            return @"
class Program
{
    void Method()
    {
        // " + (isRegularExpression ? "l" : "x") + @"anguage=regex
        _ = """ + new string(element, element == '\\' ? 2 * length : length) + @""";
    }
}
";
        }
 
        protected Task<ImmutableArray<ClassifiedSpan>> GetClassificationSpansAsync(string code, TextSpan span, ParseOptions parseOptions)
        {
            using (var workspace = TestWorkspace.CreateCSharp(code, parseOptions))
            {
                var document = workspace.CurrentSolution.GetDocument(workspace.Documents.First().Id);
                return GetSemanticClassificationsAsync(document, span);
            }
        }
 
        protected static async Task<ImmutableArray<ClassifiedSpan>> GetSemanticClassificationsAsync(Document document, TextSpan span)
        {
            var tree = await document.GetSyntaxTreeAsync();
 
            var service = document.GetLanguageService<ISyntaxClassificationService>();
            var classifiers = service.GetDefaultSyntaxClassifiers();
            var extensionManager = document.Project.Solution.Services.GetService<IExtensionManager>();
 
            using var _ = Classifier.GetPooledList(out var results);
 
            await service.AddSemanticClassificationsAsync(
                document,
                span,
                ClassificationOptions.Default,
                extensionManager.CreateNodeExtensionGetter(classifiers, c => c.SyntaxNodeTypes),
                extensionManager.CreateTokenExtensionGetter(classifiers, c => c.SyntaxTokenKinds),
                results,
                CancellationToken.None);
 
            return results.ToImmutableArray();
        }
    }
}