public RunTimer() { // start twice to improve accuracy. See AnalyzerExecutor.ExecuteAndCatchIfThrows for more details _ = SharedStopwatch.StartNew(); _timer = SharedStopwatch.StartNew(); _callback = null; }
public static async Task <SerializableUnimportedExtensionMethods> GetUnimportedExtensionMethodsInCurrentProcessAsync( Document document, int position, ITypeSymbol receiverTypeSymbol, ISet <string> namespaceInScope, ImmutableArray <ITypeSymbol> targetTypes, bool forceCacheCreation, bool hideAdvancedMembers, bool isRemote, CancellationToken cancellationToken) { var stopwatch = SharedStopwatch.StartNew(); // First find symbols of all applicable extension methods. // Workspace's syntax/symbol index is used to avoid iterating every method symbols in the solution. var symbolComputer = await SymbolComputer.CreateAsync( document, position, receiverTypeSymbol, namespaceInScope, cancellationToken).ConfigureAwait(false); var(extentsionMethodSymbols, isPartialResult) = await symbolComputer.GetExtensionMethodSymbolsAsync(forceCacheCreation, hideAdvancedMembers, cancellationToken).ConfigureAwait(false); var getSymbolsTime = stopwatch.Elapsed; stopwatch = SharedStopwatch.StartNew(); var compilation = await document.Project.GetRequiredCompilationAsync(cancellationToken).ConfigureAwait(false); var items = ConvertSymbolsToCompletionItems(compilation, extentsionMethodSymbols, targetTypes, cancellationToken); var createItemsTime = stopwatch.Elapsed; return(new SerializableUnimportedExtensionMethods(items, isPartialResult, getSymbolsTime, createItemsTime, isRemote)); }
public IdleProcessor( IAsynchronousOperationListener listener, TimeSpan backOffTimeSpan, CancellationToken cancellationToken) { Listener = listener; CancellationToken = cancellationToken; BackOffTimeSpan = backOffTimeSpan; _timeSinceLastAccess = SharedStopwatch.StartNew(); }
public Task <ImmutableArray <VSCompletionItem> > SortCompletionListAsync( IAsyncCompletionSession session, AsyncCompletionSessionInitialDataSnapshot data, CancellationToken cancellationToken) { var stopwatch = SharedStopwatch.StartNew(); // Sort by default comparer of Roslyn CompletionItem var sortedItems = data.InitialList.OrderBy(CompletionItemData.GetOrAddDummyRoslynItem).ToImmutableArray(); AsyncCompletionLogger.LogItemManagerSortTicksDataPoint((int)stopwatch.Elapsed.TotalMilliseconds); return(Task.FromResult(sortedItems)); }
protected override async Task AddCompletionItemsAsync( CompletionContext completionContext, SyntaxContext syntaxContext, HashSet <string> namespaceInScope, CancellationToken cancellationToken) { using (Logger.LogBlock(FunctionId.Completion_ExtensionMethodImportCompletionProvider_GetCompletionItemsAsync, cancellationToken)) { var syntaxFacts = completionContext.Document.GetRequiredLanguageService <ISyntaxFactsService>(); if (TryGetReceiverTypeSymbol(syntaxContext, syntaxFacts, cancellationToken, out var receiverTypeSymbol)) { var totalTime = SharedStopwatch.StartNew(); var inferredTypes = completionContext.CompletionOptions.TargetTypedCompletionFilter ? syntaxContext.InferredTypes : ImmutableArray <ITypeSymbol> .Empty; var result = await ExtensionMethodImportCompletionHelper.GetUnimportedExtensionMethodsAsync( completionContext.Document, completionContext.Position, receiverTypeSymbol, namespaceInScope, inferredTypes, forceCacheCreation : completionContext.CompletionOptions.ForceExpandedCompletionIndexCreation, hideAdvancedMembers : completionContext.CompletionOptions.HideAdvancedMembers, cancellationToken).ConfigureAwait(false); if (result is null) { return; } var receiverTypeKey = SymbolKey.CreateString(receiverTypeSymbol, cancellationToken); completionContext.AddItems(result.CompletionItems.Select(i => Convert(i, receiverTypeKey))); // report telemetry: CompletionProvidersLogger.LogExtensionMethodCompletionTicksDataPoint( totalTime.Elapsed, result.GetSymbolsTime, result.CreateItemsTime, result.IsRemote); if (result.IsPartialResult) { CompletionProvidersLogger.LogExtensionMethodCompletionPartialResultCount(); } } } }
public NodeStateTable <T> UpdateStateTable(DriverStateTable.Builder graphState, NodeStateTable <T> previousTable, CancellationToken cancellationToken) { var stopwatch = SharedStopwatch.StartNew(); var inputItems = _getInput(graphState); TimeSpan elapsedTime = stopwatch.Elapsed; // create a mutable hashset of the new items we can check against HashSet <T> itemsSet = new HashSet <T>(_inputComparer); foreach (var item in inputItems) { var added = itemsSet.Add(item); Debug.Assert(added); } var builder = graphState.CreateTableBuilder(previousTable, _name, _comparer); // We always have no inputs steps into an InputNode, but we track the difference between "no inputs" (empty collection) and "no step information" (default value) var noInputStepsStepInfo = builder.TrackIncrementalSteps ? ImmutableArray <(IncrementalGeneratorRunStep, int)> .Empty : default;
public NodeStateTable <ImmutableArray <TInput> > UpdateStateTable(DriverStateTable.Builder builder, NodeStateTable <ImmutableArray <TInput> > previousTable, CancellationToken cancellationToken) { // grab the source inputs var sourceTable = builder.GetLatestStateTableForNode(_sourceNode); // Semantics of a batch transform: // Batches will always exist (a batch of the empty table is still []) // There is only ever one input, the batch of the upstream table // - Output is cached when upstream is all cached // - Added when the previous table was empty // - Modified otherwise // update the table var newTable = builder.CreateTableBuilder(previousTable, _name); // If this execution is tracking steps, then the source table should have also tracked steps or be the empty table. Debug.Assert(!newTable.TrackIncrementalSteps || (sourceTable.HasTrackedSteps || sourceTable.IsEmpty)); var stopwatch = SharedStopwatch.StartNew(); var batchedSourceEntries = sourceTable.Batch(); var sourceValues = batchedSourceEntries.SelectAsArray(sourceEntry => sourceEntry.State != EntryState.Removed, sourceEntry => sourceEntry.Item); var sourceInputs = newTable.TrackIncrementalSteps ? batchedSourceEntries.SelectAsArray(sourceEntry => (sourceEntry.Step !, sourceEntry.OutputIndex)) : default;
public NodeStateTable <ImmutableArray <TInput> > UpdateStateTable(DriverStateTable.Builder builder, NodeStateTable <ImmutableArray <TInput> > previousTable, CancellationToken cancellationToken) { // grab the source inputs var sourceTable = builder.GetLatestStateTableForNode(_sourceNode); // Semantics of a batch transform: // Batches will always exist (a batch of the empty table is still []) // There is only ever one input, the batch of the upstream table // - Output is cached when upstream is all cached // - Added when the previous table was empty // - Modified otherwise // update the table var newTable = builder.CreateTableBuilder(previousTable, _name, _comparer); // If this execution is tracking steps, then the source table should have also tracked steps or be the empty table. Debug.Assert(!newTable.TrackIncrementalSteps || (sourceTable.HasTrackedSteps || sourceTable.IsEmpty)); var stopwatch = SharedStopwatch.StartNew(); var(sourceValues, sourceInputs) = GetValuesAndInputs(sourceTable, previousTable, newTable); if (previousTable.IsEmpty) { newTable.AddEntry(sourceValues, EntryState.Added, stopwatch.Elapsed, sourceInputs, EntryState.Added); } else if (!sourceTable.IsCached || !newTable.TryUseCachedEntries(stopwatch.Elapsed, sourceInputs)) { if (!newTable.TryModifyEntry(sourceValues, _comparer, stopwatch.Elapsed, sourceInputs, EntryState.Modified)) { newTable.AddEntry(sourceValues, EntryState.Added, stopwatch.Elapsed, sourceInputs, EntryState.Added); } } return(newTable.ToImmutableAndFree()); }
public Task <ImmutableArray <VSCompletionItem> > SortCompletionListAsync( IAsyncCompletionSession session, AsyncCompletionSessionInitialDataSnapshot data, CancellationToken cancellationToken) { var stopwatch = SharedStopwatch.StartNew(); var sessionData = CompletionSessionData.GetOrCreateSessionData(session); // This method is called exactly once, so use the opportunity to set a baseline for telemetry. if (sessionData.TargetTypeFilterExperimentEnabled) { AsyncCompletionLogger.LogSessionHasTargetTypeFilterEnabled(); if (data.InitialList.Any(i => i.Filters.Any(f => f.DisplayText == FeaturesResources.Target_type_matches))) { AsyncCompletionLogger.LogSessionContainsTargetTypeFilter(); } } // Sort by default comparer of Roslyn CompletionItem var sortedItems = data.InitialList.OrderBy(CompletionItemData.GetOrAddDummyRoslynItem).ToImmutableArray(); AsyncCompletionLogger.LogItemManagerSortTicksDataPoint((int)stopwatch.Elapsed.TotalMilliseconds); return(Task.FromResult(sortedItems)); }
protected override async Task ProduceTagsAsync( TaggerContext <InheritanceMarginTag> context, DocumentSnapshotSpan spanToTag, int?caretPosition, CancellationToken cancellationToken) { var document = spanToTag.Document; if (document == null) { return; } var inheritanceMarginInfoService = document.GetLanguageService <IInheritanceMarginService>(); if (inheritanceMarginInfoService == null) { return; } if (GlobalOptions.GetOption(FeatureOnOffOptions.ShowInheritanceMargin, document.Project.Language) == false) { return; } var includeGlobalImports = GlobalOptions.GetOption(FeatureOnOffOptions.InheritanceMarginIncludeGlobalImports, document.Project.Language); // Use FrozenSemantics Version of document to get the semantics ready, therefore we could have faster // response. (Since the full load might take a long time) // We also subscribe to CompilationAvailableTaggerEventSource, so this will finally reach the correct state. document = document.WithFrozenPartialSemantics(cancellationToken); var spanToSearch = spanToTag.SnapshotSpan.Span.ToTextSpan(); var stopwatch = SharedStopwatch.StartNew(); var inheritanceMemberItems = await inheritanceMarginInfoService.GetInheritanceMemberItemsAsync( document, spanToSearch, includeGlobalImports, cancellationToken).ConfigureAwait(false); var elapsed = stopwatch.Elapsed; if (inheritanceMemberItems.IsEmpty) { return; } InheritanceMarginLogger.LogGenerateBackgroundInheritanceInfo(elapsed); // One line might have multiple members to show, so group them. // For example: // interface IBar { void Foo1(); void Foo2(); } // class Bar : IBar { void Foo1() { } void Foo2() { } } var lineToMembers = inheritanceMemberItems.GroupBy(item => item.LineNumber); var snapshot = spanToTag.SnapshotSpan.Snapshot; foreach (var(lineNumber, membersOnTheLine) in lineToMembers) { var membersOnTheLineArray = membersOnTheLine.ToImmutableArray(); // One line should at least have one member on it. Contract.ThrowIfTrue(membersOnTheLineArray.IsEmpty); var line = snapshot.GetLineFromLineNumber(lineNumber); // We only care about the line, so just tag the start. context.AddTag(new TagSpan <InheritanceMarginTag>( new SnapshotSpan(snapshot, line.Start, length: 0), new InheritanceMarginTag(document.Project.Solution.Workspace, lineNumber, membersOnTheLineArray))); } }
public RunTimer() { _timer = SharedStopwatch.StartNew(); _callback = null; _adjustRunTime = null; }
public async Task <FilteredCompletionModel?> UpdateCompletionListAsync( IAsyncCompletionSession session, AsyncCompletionSessionDataSnapshot data, CancellationToken cancellationToken) { var stopwatch = SharedStopwatch.StartNew(); try { var sessionData = CompletionSessionData.GetOrCreateSessionData(session); // As explained in more details in the comments for `CompletionSource.GetCompletionContextAsync`, expanded items might // not be provided upon initial trigger of completion to reduce typing delays, even if they are supposed to be included by default. // While we do not expect to run in to this scenario very often, we'd still want to minimize the impact on user experience of this feature // as best as we could when it does occur. So the solution we came up with is this: if we decided to not include expanded items (because the // computation is running too long,) we will let it run in the background as long as the completion session is still active. Then whenever // any user input that would cause the completion list to refresh, we will check the state of this background task and add expanded items as part // of the update if they are available. // There is a `CompletionContext.IsIncomplete` flag, which is only supported in LSP mode at the moment. Therefore we opt to handle the checking // and combining the items in Roslyn until the `IsIncomplete` flag is fully supported in classic mode. if (sessionData.CombinedSortedList.HasValue) { // Always use the previously saved combined list if available. data = new AsyncCompletionSessionDataSnapshot(sessionData.CombinedSortedList.Value, data.Snapshot, data.Trigger, data.InitialTrigger, data.SelectedFilters, data.IsSoftSelected, data.DisplaySuggestionItem, data.Defaults); } else if (sessionData.ExpandedItemsTask != null) { var task = sessionData.ExpandedItemsTask; if (task.Status == TaskStatus.RanToCompletion) { // Make sure the task is removed when Adding expanded items, // so duplicated items won't be added in subsequent list updates. sessionData.ExpandedItemsTask = null; var(expandedContext, _) = await task.ConfigureAwait(false); if (expandedContext.Items.Length > 0) { // Here we rely on the implementation detail of `CompletionItem.CompareTo`, which always put expand items after regular ones. var itemsBuilder = ImmutableArray.CreateBuilder <VSCompletionItem>(expandedContext.Items.Length + data.InitialSortedList.Length); itemsBuilder.AddRange(data.InitialSortedList); itemsBuilder.AddRange(expandedContext.Items); var combinedList = itemsBuilder.MoveToImmutable(); // Add expanded items into a combined list, and save it to be used for future updates during the same session. sessionData.CombinedSortedList = combinedList; var combinedFilterStates = FilterSet.CombineFilterStates(expandedContext.Filters, data.SelectedFilters); data = new AsyncCompletionSessionDataSnapshot(combinedList, data.Snapshot, data.Trigger, data.InitialTrigger, combinedFilterStates, data.IsSoftSelected, data.DisplaySuggestionItem, data.Defaults); } AsyncCompletionLogger.LogSessionWithDelayedImportCompletionIncludedInUpdate(); } } var updater = new CompletionListUpdater(session.ApplicableToSpan, sessionData, data, _recentItemsManager, _globalOptions); return(updater.UpdateCompletionList(cancellationToken)); } finally { AsyncCompletionLogger.LogItemManagerUpdateDataPoint((int)stopwatch.Elapsed.TotalMilliseconds, isCanceled: cancellationToken.IsCancellationRequested); } }
public IStateTable GetSyntaxInputTable(SyntaxInputNode syntaxInputNode, NodeStateTable <SyntaxTree> syntaxTreeTable) { Debug.Assert(_syntaxInputNodes.Contains(syntaxInputNode)); // when we don't have a value for this node, we update all the syntax inputs at once if (!_tableBuilder.Contains(syntaxInputNode)) { // CONSIDER: when the compilation is the same as previous, the syntax trees must also be the same. // if we have a previous state table for a node, we can just short circuit knowing that it is up to date // This step isn't part of the tree, so we can skip recording. var compilationIsCached = _compilation == _previous._compilation; // get a builder for each input node var syntaxInputBuilders = ArrayBuilder <(SyntaxInputNode node, ISyntaxInputBuilder builder)> .GetInstance(_syntaxInputNodes.Length); foreach (var node in _syntaxInputNodes) { // We don't cache the tracked incremental steps in a manner that we can easily rehydrate between runs, // so we disable the cached compilation perf optimization when incremental step tracking is enabled. if (compilationIsCached && !_enableTracking && _previous._tables.TryGetValue(node, out var previousStateTable)) { _tableBuilder.SetTable(node, previousStateTable); } else { syntaxInputBuilders.Add((node, node.GetBuilder(_previous._tables, _enableTracking))); _syntaxTimes[node] = TimeSpan.Zero; } } if (syntaxInputBuilders.Count > 0) { // at this point we need to grab the syntax trees from the new compilation, and optionally diff them against the old ones NodeStateTable <SyntaxTree> syntaxTreeState = syntaxTreeTable; // update each tree for the builders, sharing the semantic model foreach (var(tree, state, syntaxTreeIndex, stepInfo) in syntaxTreeState) { var root = new Lazy <SyntaxNode>(() => tree.GetRoot(_cancellationToken)); var model = state != EntryState.Removed ? new Lazy <SemanticModel>(() => _compilation.GetSemanticModel(tree)) : null; for (int i = 0; i < syntaxInputBuilders.Count; i++) { var currentNode = syntaxInputBuilders[i].node; try { var sw = SharedStopwatch.StartNew(); try { _cancellationToken.ThrowIfCancellationRequested(); syntaxInputBuilders[i].builder.VisitTree(root, state, model, _cancellationToken); } finally { var elapsed = sw.Elapsed; // if this node isn't the one that caused the update, ensure we remember it and remove the time it took from the requester if (currentNode != syntaxInputNode) { _syntaxTimes[syntaxInputNode] = _syntaxTimes[syntaxInputNode].Subtract(elapsed); _syntaxTimes[currentNode] = _syntaxTimes[currentNode].Add(elapsed); } } } catch (UserFunctionException ufe) { // we're evaluating this node ahead of time, so we can't just throw the exception // instead we'll hold onto it, and throw the exception when a downstream node actually // attempts to read the value _syntaxExceptions[currentNode] = ufe; syntaxInputBuilders.RemoveAt(i); i--; } } } // save the updated inputs foreach ((var node, ISyntaxInputBuilder builder) in syntaxInputBuilders) { builder.SaveStateAndFree(_tableBuilder); Debug.Assert(_tableBuilder.Contains(node)); } } syntaxInputBuilders.Free(); } // if we don't have an entry for this node, it must have thrown an exception if (!_tableBuilder.TryGetTable(syntaxInputNode, out var result)) { throw _syntaxExceptions[syntaxInputNode]; } return(result); }
protected void UpdateLastAccessTime() => _timeSinceLastAccess = SharedStopwatch.StartNew();
private async ValueTask ProcessNextDocumentBatchAsync( ImmutableArray <DocumentId> documentIds, CancellationToken cancellationToken) { // This logic preserves the previous behavior we had with IForegroundNotificationService. // Specifically, we don't run on the UI thread for more than 15ms at a time. And once we // have, we wait 50ms before continuing. These constants are just what we defined from // legacy, and otherwise have no special meaning. const int MaxTimeSlice = 15; var delayBetweenProcessing = TimeSpan.FromMilliseconds(50); Debug.Assert(!_threadingContext.JoinableTaskContext.IsOnMainThread, "The following context switch is not expected to cause runtime overhead."); await TaskScheduler.Default; // Ensure MEF services used by the code model are initially obtained on a background thread. // This code avoids allocations where possible. // https://github.com/dotnet/roslyn/issues/54159 string?previousLanguage = null; foreach (var(_, projectState) in _visualStudioWorkspace.CurrentSolution.State.ProjectStates) { if (projectState.Language == previousLanguage) { // Avoid duplicate calls if the language did not change continue; } previousLanguage = projectState.Language; projectState.LanguageServices.GetService <ICodeModelService>(); projectState.LanguageServices.GetService <ISyntaxFactsService>(); projectState.LanguageServices.GetService <ICodeGenerationService>(); } await _threadingContext.JoinableTaskFactory.SwitchToMainThreadAsync(cancellationToken); var stopwatch = SharedStopwatch.StartNew(); foreach (var documentId in documentIds) { FireEventsForDocument(documentId); // Keep firing events for this doc, as long as we haven't exceeded the max amount // of waiting time, and there's no user input that should take precedence. if (stopwatch.Elapsed.Ticks > MaxTimeSlice || IsInputPending()) { await this.Listener.Delay(delayBetweenProcessing, cancellationToken).ConfigureAwait(true); stopwatch = SharedStopwatch.StartNew(); } } return; void FireEventsForDocument(DocumentId documentId) { // If we've been asked to shutdown, don't bother reporting any more events. if (_threadingContext.DisposalToken.IsCancellationRequested) { return; } var projectCodeModel = this.TryGetProjectCodeModel(documentId.ProjectId); if (projectCodeModel == null) { return; } var filename = _visualStudioWorkspace.GetFilePath(documentId); if (filename == null) { return; } if (!projectCodeModel.TryGetCachedFileCodeModel(filename, out var fileCodeModelHandle)) { return; } var codeModel = fileCodeModelHandle.Object; codeModel.FireEvents(); return; } }
private async Task ProcessNextDocumentBatchAsync( ImmutableArray <DocumentId> documentIds, CancellationToken cancellationToken) { // This logic preserves the previous behavior we had with IForegroundNotificationService. // Specifically, we don't run on the UI thread for more than 15ms at a time. And once we // have, we wait 50ms before continuing. These constants are just what we defined from // legacy, and otherwise have no special meaning. const int MaxTimeSlice = 15; var delayBetweenProcessing = TimeSpan.FromMilliseconds(50); await _threadingContext.JoinableTaskFactory.SwitchToMainThreadAsync(cancellationToken); var stopwatch = SharedStopwatch.StartNew(); foreach (var documentId in documentIds) { FireEventsForDocument(documentId); // Keep firing events for this doc, as long as we haven't exceeded the max amount // of waiting time, and there's no user input that should take precedence. if (stopwatch.Elapsed.Ticks > MaxTimeSlice || IsInputPending()) { await this.Listener.Delay(delayBetweenProcessing, cancellationToken).ConfigureAwait(true); stopwatch = SharedStopwatch.StartNew(); } } return; void FireEventsForDocument(DocumentId documentId) { // If we've been asked to shutdown, don't bother reporting any more events. if (_threadingContext.DisposalToken.IsCancellationRequested) { return; } var projectCodeModel = this.TryGetProjectCodeModel(documentId.ProjectId); if (projectCodeModel == null) { return; } var filename = _visualStudioWorkspace.GetFilePath(documentId); if (filename == null) { return; } if (!projectCodeModel.TryGetCachedFileCodeModel(filename, out var fileCodeModelHandle)) { return; } var codeModel = fileCodeModelHandle.Object; codeModel.FireEvents(); return; } }
public RequestMetrics(string methodName, RequestTelemetryLogger requestTelemetryLogger) { _methodName = methodName; _requestTelemetryLogger = requestTelemetryLogger; _sharedStopWatch = SharedStopwatch.StartNew(); }