Example #1
0
        public NodeStateTable <TOutput> UpdateStateTable(DriverStateTable.Builder builder, NodeStateTable <TOutput> previousTable, CancellationToken cancellationToken)
        {
            // grab the source inputs
            var sourceTable = builder.GetLatestStateTableForNode(_sourceNode);

            if (sourceTable.IsCached)
            {
                if (builder.DriverState.TrackIncrementalSteps)
                {
                    return(previousTable.CreateCachedTableWithUpdatedSteps(sourceTable, _name));
                }
                return(previousTable);
            }

            // Semantics of a transform:
            // Element-wise comparison of upstream table
            // - Cached or Removed: no transform, just use previous values
            // - Added: perform transform and add
            // - Modified: perform transform and do element wise comparison with previous results

            var newTable = builder.CreateTableBuilder(previousTable, _name);

            foreach (var entry in sourceTable)
            {
                var inputs = newTable.TrackIncrementalSteps ? ImmutableArray.Create((entry.Step !, entry.OutputIndex)) : default;
Example #2
0
        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

            var source = sourceTable.Batch();

            // update the table
            var newTable = previousTable.ToBuilder();

            if (!sourceTable.IsCached || !newTable.TryUseCachedEntries())
            {
                if (!newTable.TryModifyEntry(source, _comparer))
                {
                    newTable.AddEntry(source, EntryState.Added);
                }
            }

            return(newTable.ToImmutableAndFree());
        }
Example #3
0
        public NodeStateTable <T> UpdateStateTable(DriverStateTable.Builder graphState, NodeStateTable <T> previousTable, CancellationToken cancellationToken)
        {
            var inputItems = _getInput(graphState);

            // create a mutable hashset of the new items we can check against
            HashSet <T> itemsSet = new HashSet <T>(_comparer);

            foreach (var item in inputItems)
            {
                var added = itemsSet.Add(item);
                Debug.Assert(added);
            }

            var builder = previousTable.ToBuilder();

            // for each item in the previous table, check if its still in the new items
            int itemIndex = 0;

            foreach ((var oldItem, _) in previousTable)
            {
                if (itemsSet.Remove(oldItem))
                {
                    // we're iterating the table, so know that it has entries
                    var usedCache = builder.TryUseCachedEntries();
                    Debug.Assert(usedCache);
                }
                else if (inputItems.Length == previousTable.Count)
                {
                    // When the number of items matches the previous iteration, we use a heuristic to mark the input as modified
                    // This allows us to correctly 'replace' items even when they aren't actually the same. In the case that the
                    // item really isn't modified, but a new item, we still function correctly as we mostly treat them the same,
                    // but will perform an extra comparison that is omitted in the pure 'added' case.
                    var modified = builder.TryModifyEntry(inputItems[itemIndex], _comparer);
                    Debug.Assert(modified);
                    itemsSet.Remove(inputItems[itemIndex]);
                }
                else
                {
                    builder.RemoveEntries();
                }
                itemIndex++;
            }

            // any remaining new items are added
            foreach (var newItem in itemsSet)
            {
                builder.AddEntry(newItem, EntryState.Added);
            }

            return(builder.ToImmutableAndFree());
        }
Example #4
0
        public NodeStateTable <TOutput> UpdateStateTable(DriverStateTable.Builder graphState, NodeStateTable <TOutput> previousTable, CancellationToken cancellationToken)
        {
            var sourceTable = graphState.GetLatestStateTableForNode(_source);

            if (sourceTable.IsCached)
            {
                return(previousTable);
            }

            var nodeTable = previousTable.ToBuilder();

            foreach (var entry in sourceTable)
            {
                if (entry.state == EntryState.Removed)
                {
                    nodeTable.RemoveEntries();
                }
                else if (entry.state != EntryState.Cached || !nodeTable.TryUseCachedEntries())
                {
                    // we don't currently handle modified any differently than added at the output
                    // we just run the action and mark the new source as added. In theory we could compare
                    // the diagnostics and sources produced and compare them, to see if they are any different
                    // than before.

                    var sourcesBuilder = ArrayBuilder <GeneratedSourceText> .GetInstance();

                    var diagnostics = DiagnosticBag.GetInstance();

                    SourceProductionContext context = new SourceProductionContext(sourcesBuilder, diagnostics, cancellationToken);
                    try
                    {
                        _action(context, entry.item);
                        nodeTable.AddEntry((sourcesBuilder.ToImmutable(), diagnostics.ToReadOnly()), EntryState.Added);
                    }
                    finally
                    {
                        sourcesBuilder.Free();
                        diagnostics.Free();
                    }
                }
            }

            return(nodeTable.ToImmutableAndFree());
        }
        public NodeStateTable <TOutput> UpdateStateTable(DriverStateTable.Builder graphState, NodeStateTable <TOutput> previousTable, CancellationToken cancellationToken)
        {
            string stepName    = Kind == IncrementalGeneratorOutputKind.Source ? WellKnownGeneratorOutputs.SourceOutput : WellKnownGeneratorOutputs.ImplementationSourceOutput;
            var    sourceTable = graphState.GetLatestStateTableForNode(_source);

            if (sourceTable.IsCached)
            {
                if (graphState.DriverState.TrackIncrementalSteps)
                {
                    return(previousTable.CreateCachedTableWithUpdatedSteps(sourceTable, stepName));
                }
                return(previousTable);
            }

            var nodeTable = graphState.CreateTableBuilder(previousTable, stepName);

            foreach (var entry in sourceTable)
            {
                var inputs = nodeTable.TrackIncrementalSteps ? ImmutableArray.Create((entry.Step !, entry.OutputIndex)) : default;
Example #6
0
        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;
Example #7
0
        public NodeStateTable <T> UpdateStateTable(DriverStateTable.Builder graphState, NodeStateTable <T> previousTable, CancellationToken cancellationToken)
        {
            var inputItems = _getInput(graphState);

            // create a mutable hashset of the new items we can check against
            HashSet <T> itemsSet = new HashSet <T>(_comparer);

            foreach (var item in inputItems)
            {
                var added = itemsSet.Add(item);
                Debug.Assert(added);
            }

            var builder = previousTable.ToBuilder();

            // for each item in the previous table, check if its still in the new items
            foreach ((var oldItem, _) in previousTable)
            {
                if (itemsSet.Remove(oldItem))
                {
                    // we're iterating the table, so know that it has entries
                    var usedCache = builder.TryUseCachedEntries();
                    Debug.Assert(usedCache);
                }
                else
                {
                    builder.RemoveEntries();
                }
            }

            // any remaining new items are added
            foreach (var newItem in itemsSet)
            {
                builder.AddEntry(newItem, EntryState.Added);
            }

            return(builder.ToImmutableAndFree());
        }
Example #8
0
        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;
Example #9
0
        public NodeStateTable <TOutput> UpdateStateTable(DriverStateTable.Builder builder, NodeStateTable <TOutput> previousTable, CancellationToken cancellationToken)
        {
            // grab the source inputs
            var sourceTable = builder.GetLatestStateTableForNode(_sourceNode);

            if (sourceTable.IsCached)
            {
                return(previousTable);
            }

            // Semantics of a transform:
            // Element-wise comparison of upstream table
            // - Cached or Removed: no transform, just use previous values
            // - Added: perform transform and add
            // - Modified: perform transform and do element wise comparison with previous results

            var newTable = previousTable.ToBuilder();

            foreach (var entry in sourceTable)
            {
                if (entry.state == EntryState.Removed)
                {
                    newTable.RemoveEntries();
                }
                else if (entry.state != EntryState.Cached || !newTable.TryUseCachedEntries())
                {
                    // generate the new entries
                    var newOutputs = _func(entry.item, cancellationToken);

                    if (entry.state != EntryState.Modified || !newTable.TryModifyEntries(newOutputs, _comparer))
                    {
                        newTable.AddEntries(newOutputs, EntryState.Added);
                    }
                }
            }
            return(newTable.ToImmutableAndFree());
        }
Example #10
0
        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());
        }
Example #11
0
 public NodeStateTable <T> UpdateStateTable(DriverStateTable.Builder graphState, NodeStateTable <T> previousTable, CancellationToken cancellationToken)
 {
     return((NodeStateTable <T>)graphState.GetSyntaxInputTable(this));
 }