//--------------------------------------------------------------------------------------- // MoveNext will invoke the entire query sub-tree, accumulating results into a hash- // table, upon the first call. Then for the first call and all subsequent calls, we will // just enumerate the key-set from the hash-table, retrieving groupings of key-elements. // internal override bool MoveNext(ref IGrouping <TGroupKey, TElement> currentElement, ref TOrderKey currentKey) { Debug.Assert(_source != null); Debug.Assert(_keySelector != null); // Lazy-init the mutable state. This also means we haven't yet built our lookup of // groupings, so we can go ahead and do that too. Mutables mutables = _mutables; if (mutables == null) { mutables = _mutables = new Mutables(); // Build the hash lookup and start enumerating the lookup at the beginning. mutables._hashLookup = BuildHashLookup(); Debug.Assert(mutables._hashLookup != null); mutables._hashLookupIndex = -1; } // Now, with a hash lookup in hand, we just enumerate the keys. So long // as the key-value lookup has elements, we have elements. if (++mutables._hashLookupIndex < mutables._hashLookup.Count) { GroupKeyData value = mutables._hashLookup[mutables._hashLookupIndex].Value; currentElement = value._grouping; currentKey = value._orderKey; return(true); } return(false); }
internal override bool MoveNext(ref Pair <TInputOutput, THashKey> currentElement, ref TOrderKey currentKey) { if (this.m_partitionCount == 1) { TInputOutput local = default(TInputOutput); if (this.m_source.MoveNext(ref local, ref currentKey)) { currentElement = new Pair <TInputOutput, THashKey>(local, (this.m_keySelector == null) ? default(THashKey) : this.m_keySelector(local)); return(true); } return(false); } Mutables <TInputOutput, THashKey, TOrderKey> mutables = this.m_mutables; if (mutables == null) { mutables = this.m_mutables = new Mutables <TInputOutput, THashKey, TOrderKey>(); } if (mutables.m_currentBufferIndex == -1) { this.EnumerateAndRedistributeElements(); } while (mutables.m_currentBufferIndex < this.m_partitionCount) { if (mutables.m_currentBuffer != null) { if (++mutables.m_currentIndex < mutables.m_currentBuffer.Count) { currentElement = mutables.m_currentBuffer.m_chunk[mutables.m_currentIndex]; currentKey = mutables.m_currentKeyBuffer.m_chunk[mutables.m_currentIndex]; return(true); } mutables.m_currentIndex = -1; mutables.m_currentBuffer = mutables.m_currentBuffer.Next; mutables.m_currentKeyBuffer = mutables.m_currentKeyBuffer.Next; } else { if (mutables.m_currentBufferIndex == this.m_partitionIndex) { this.m_barrier.Wait(this.m_cancellationToken); mutables.m_currentBufferIndex = -1; } mutables.m_currentBufferIndex++; mutables.m_currentIndex = -1; if (mutables.m_currentBufferIndex == this.m_partitionIndex) { mutables.m_currentBufferIndex++; } if (mutables.m_currentBufferIndex < this.m_partitionCount) { mutables.m_currentBuffer = this.m_valueExchangeMatrix[mutables.m_currentBufferIndex, this.m_partitionIndex]; mutables.m_currentKeyBuffer = this.m_keyExchangeMatrix[mutables.m_currentBufferIndex, this.m_partitionIndex]; } } } return(false); }
internal override bool MoveNext(ref T currentElement, ref int currentKey) { Mutables <T> mutables = this.m_mutables; if (mutables == null) { mutables = this.m_mutables = new Mutables <T>(); } while (true) { T[] chunkBuffer = mutables.m_chunkBuffer; int index = ++mutables.m_currentChunkIndex; if (index < mutables.m_currentChunkSize) { currentElement = chunkBuffer[index]; currentKey = mutables.m_chunkBaseIndex + index; return(true); } lock (this.m_sourceSyncLock) { int num2 = 0; if (this.m_exceptionTracker.Value) { return(false); } try { while ((num2 < mutables.m_nextChunkMaxSize) && this.m_source.MoveNext()) { chunkBuffer[num2] = this.m_source.Current; num2++; } } catch { this.m_exceptionTracker.Value = true; throw; } mutables.m_currentChunkSize = num2; if (num2 == 0) { return(false); } mutables.m_chunkBaseIndex = this.m_currentIndex.Value; this.m_currentIndex.Value += num2; } if ((mutables.m_nextChunkMaxSize < chunkBuffer.Length) && ((mutables.m_chunkCounter++ & 7) == 7)) { mutables.m_nextChunkMaxSize *= 2; if (mutables.m_nextChunkMaxSize > chunkBuffer.Length) { mutables.m_nextChunkMaxSize = chunkBuffer.Length; } } mutables.m_currentChunkIndex = -1; } }
private bool MoveNextSlowPath() { Mutables mutables = m_mutables; Contract.Assert(mutables != null); Contract.Assert(mutables.m_currentPositionInChunk >= mutables.m_currentChunkSize); // Move on to the next section. int currentSection = ++mutables.m_currentSection; int sectionsRemaining = m_sectionCount - currentSection; // If empty, return right away. if (sectionsRemaining <= 0) { return(false); } // Compute the offset of the current section from the beginning of the range int currentSectionOffset = currentSection * m_partitionCount * m_maxChunkSize; mutables.m_currentPositionInChunk = 0; // Now do something different based on how many chunks remain. if (sectionsRemaining > 1) { // We are not on the last section. The size of this chunk is simply m_maxChunkSize. mutables.m_currentChunkSize = m_maxChunkSize; mutables.m_currentChunkOffset = currentSectionOffset + m_partitionIndex * m_maxChunkSize; } else { // We are on the last section. Compute the size of the chunk to ensure even distribution // of elements. int lastSectionElementCount = m_elementCount - currentSectionOffset; int smallerChunkSize = lastSectionElementCount / m_partitionCount; int biggerChunkCount = lastSectionElementCount % m_partitionCount; mutables.m_currentChunkSize = smallerChunkSize; if (m_partitionIndex < biggerChunkCount) { mutables.m_currentChunkSize++; } if (mutables.m_currentChunkSize == 0) { return(false); } mutables.m_currentChunkOffset = currentSectionOffset // The beginning of this section + m_partitionIndex * smallerChunkSize // + the start of this chunk if all chunks were "smaller" + (m_partitionIndex < biggerChunkCount ? m_partitionIndex : biggerChunkCount); // + the number of "bigger" chunks before this chunk } return(true); }
public void RegisterMutableAction(Type type, IMutableElement mutableElement) { if (Mutables.ContainsKey(type)) { Mutables[type] = mutableElement; } else { Mutables.Add(type, mutableElement); } }
internal override bool MoveNext([MaybeNullWhen(false), AllowNull] ref T currentElement, ref int currentKey) { // Lazily allocate the mutable holder. Mutables mutables = _mutables ??= new Mutables(); // If we are aren't within the chunk, we need to find another. if (++mutables._currentPositionInChunk < mutables._currentChunkSize || MoveNextSlowPath()) { currentKey = mutables._currentChunkOffset + mutables._currentPositionInChunk; currentElement = _data[currentKey]; return(true); } return(false); }
private void EnumerateAndRedistributeElements() { Mutables <TInputOutput, THashKey, TOrderKey> mutables = this.m_mutables; ListChunk <Pair <TInputOutput, THashKey> >[] chunkArray = new ListChunk <Pair <TInputOutput, THashKey> > [this.m_partitionCount]; ListChunk <TOrderKey>[] chunkArray2 = new ListChunk <TOrderKey> [this.m_partitionCount]; TInputOutput currentElement = default(TInputOutput); TOrderKey currentKey = default(TOrderKey); int num = 0; while (this.m_source.MoveNext(ref currentElement, ref currentKey)) { int num2; if ((num++ & 0x3f) == 0) { CancellationState.ThrowIfCanceled(this.m_cancellationToken); } THashKey key = default(THashKey); if (this.m_keySelector != null) { key = this.m_keySelector(currentElement); num2 = this.m_repartitionStream.GetHashCode(key) % this.m_partitionCount; } else { num2 = this.m_repartitionStream.GetHashCode(currentElement) % this.m_partitionCount; } ListChunk <Pair <TInputOutput, THashKey> > chunk = chunkArray[num2]; ListChunk <TOrderKey> chunk2 = chunkArray2[num2]; if (chunk == null) { chunkArray[num2] = chunk = new ListChunk <Pair <TInputOutput, THashKey> >(0x80); chunkArray2[num2] = chunk2 = new ListChunk <TOrderKey>(0x80); } chunk.Add(new Pair <TInputOutput, THashKey>(currentElement, key)); chunk2.Add(currentKey); } for (int i = 0; i < this.m_partitionCount; i++) { this.m_valueExchangeMatrix[this.m_partitionIndex, i] = chunkArray[i]; this.m_keyExchangeMatrix[this.m_partitionIndex, i] = chunkArray2[i]; } this.m_barrier.Signal(); mutables.m_currentBufferIndex = this.m_partitionIndex; mutables.m_currentBuffer = chunkArray[this.m_partitionIndex]; mutables.m_currentKeyBuffer = chunkArray2[this.m_partitionIndex]; mutables.m_currentIndex = -1; }
internal override bool MoveNext(ref T currentElement, ref int currentKey) { Mutables <T> mutables = this.m_mutables; if (mutables == null) { mutables = this.m_mutables = new Mutables <T>(); } if ((++mutables.m_currentPositionInChunk >= mutables.m_currentChunkSize) && !this.MoveNextSlowPath()) { return(false); } currentKey = mutables.m_currentChunkOffset + mutables.m_currentPositionInChunk; currentElement = this.m_data[currentKey]; return(true); }
internal override bool MoveNext(ref IGrouping <TGroupKey, TElement> currentElement, ref TOrderKey currentKey) { Mutables <TSource, TGroupKey, TElement, TOrderKey> mutables = this.m_mutables; if (mutables == null) { mutables = this.m_mutables = new Mutables <TSource, TGroupKey, TElement, TOrderKey>(); mutables.m_hashLookup = this.BuildHashLookup(); mutables.m_hashLookupIndex = -1; } if (++mutables.m_hashLookupIndex < mutables.m_hashLookup.Count) { currentElement = new GroupByGrouping <TGroupKey, TElement>(mutables.m_hashLookup[mutables.m_hashLookupIndex]); return(true); } return(false); }
internal override bool MoveNext(ref IGrouping <TGroupKey, TElement> currentElement, ref TOrderKey currentKey) { Mutables <TSource, TGroupKey, TElement, TOrderKey> mutables = this.m_mutables; if (mutables == null) { mutables = this.m_mutables = new Mutables <TSource, TGroupKey, TElement, TOrderKey>(); mutables.m_hashLookup = this.BuildHashLookup(); mutables.m_hashLookupIndex = -1; } if (++mutables.m_hashLookupIndex < mutables.m_hashLookup.Count) { KeyValuePair <Wrapper <TGroupKey>, GroupKeyData <TSource, TGroupKey, TElement, TOrderKey> > pair = mutables.m_hashLookup[mutables.m_hashLookupIndex]; GroupKeyData <TSource, TGroupKey, TElement, TOrderKey> data = pair.Value; currentElement = data.m_grouping; currentKey = data.m_orderKey; return(true); } return(false); }
internal override bool MoveNext(ref T currentElement, ref int currentKey) { // Lazily allocate the mutable holder. Mutables mutables = m_mutables; if (mutables == null) { mutables = m_mutables = new Mutables(); } // If we are aren't within the chunk, we need to find another. if (++mutables.m_currentPositionInChunk < mutables.m_currentChunkSize || MoveNextSlowPath()) { currentKey = mutables.m_currentChunkOffset + mutables.m_currentPositionInChunk; currentElement = m_data[currentKey]; return(true); } return(false); }
internal override bool MoveNext(ref TOutput currentElement, ref Pair <TLeftKey, int> currentKey) { while (true) { if (this.m_currentRightSource == null) { this.m_mutables = new Mutables <TLeftInput, TRightInput, TOutput, TLeftKey>(); if ((this.m_mutables.m_lhsCount++ & 0x3f) == 0) { CancellationState.ThrowIfCanceled(this.m_cancellationToken); } if (!this.m_leftSource.MoveNext(ref this.m_mutables.m_currentLeftElement, ref this.m_mutables.m_currentLeftKey)) { return(false); } this.m_currentRightSource = this.m_selectManyOperator.m_rightChildSelector(this.m_mutables.m_currentLeftElement).GetEnumerator(); if (this.m_selectManyOperator.m_resultSelector == null) { this.m_currentRightSourceAsOutput = (IEnumerator <TOutput>) this.m_currentRightSource; } } if (this.m_currentRightSource.MoveNext()) { this.m_mutables.m_currentRightSourceIndex++; if (this.m_selectManyOperator.m_resultSelector != null) { currentElement = this.m_selectManyOperator.m_resultSelector(this.m_mutables.m_currentLeftElement, this.m_currentRightSource.Current); } else { currentElement = this.m_currentRightSourceAsOutput.Current; } currentKey = new Pair <TLeftKey, int>(this.m_mutables.m_currentLeftKey, this.m_mutables.m_currentRightSourceIndex); return(true); } this.m_currentRightSource.Dispose(); this.m_currentRightSource = null; this.m_currentRightSourceAsOutput = null; } }
protected T CreateNewInstance(IMap <string, INode> childNodes, IStateTreeNode meta) { var observables = Mutables.Select(mutable => new ObservableProperty { Type = mutable.Kind, Name = mutable.Name, Default = mutable.Default }).ToList(); var volatiles = Volatiles.Select(xvolatile => new Observable.VolatileProperty { Type = xvolatile.Kind, Name = xvolatile.Name, Default = xvolatile.Default }).ToList(); var computeds = Views.Select(view => new ComputedProperty { Type = view.Kind, Name = view.Name, Compute = view.View }).ToList(); // var actions = Actions.Select(action => new ActionMethod { Name = action.Name, Action = action.Action }); ObservableTypeDef typeDef = new ObservableTypeDef(observables, volatiles, computeds); var instance = ObservableObject <T, INode> .FromAs(typeDef, Proxify, Name, this, meta); return(instance); }
private bool MoveNextSlowPath() { Mutables <T> mutables = this.m_mutables; int num = ++mutables.m_currentSection; int num2 = this.m_sectionCount - num; if (num2 <= 0) { return(false); } int num3 = (num * this.m_partitionCount) * this.m_maxChunkSize; mutables.m_currentPositionInChunk = 0; if (num2 > 1) { mutables.m_currentChunkSize = this.m_maxChunkSize; mutables.m_currentChunkOffset = num3 + (this.m_partitionIndex * this.m_maxChunkSize); } else { int num4 = this.m_elementCount - num3; int num5 = num4 / this.m_partitionCount; int num6 = num4 % this.m_partitionCount; mutables.m_currentChunkSize = num5; if (this.m_partitionIndex < num6) { mutables.m_currentChunkSize++; } if (mutables.m_currentChunkSize == 0) { return(false); } mutables.m_currentChunkOffset = (num3 + (this.m_partitionIndex * num5)) + ((this.m_partitionIndex < num6) ? this.m_partitionIndex : num6); } return(true); }
//--------------------------------------------------------------------------------------- // Just retrieves the current element from our current chunk. // internal override bool MoveNext(ref T currentElement, ref int currentKey) { Mutables mutables = m_mutables; if (mutables == null) { mutables = m_mutables = new Mutables(); } Contract.Assert(mutables.m_chunkBuffer != null); // Loop until we've exhausted our data source. while (true) { // If we have elements remaining in the current chunk, return right away. T[] chunkBuffer = mutables.m_chunkBuffer; int currentChunkIndex = ++mutables.m_currentChunkIndex; if (currentChunkIndex < mutables.m_currentChunkSize) { Contract.Assert(m_source != null); Contract.Assert(chunkBuffer != null); Contract.Assert(mutables.m_currentChunkSize > 0); Contract.Assert(0 <= currentChunkIndex && currentChunkIndex < chunkBuffer.Length); currentElement = chunkBuffer[currentChunkIndex]; currentKey = mutables.m_chunkBaseIndex + currentChunkIndex; return(true); } // Else, it could be the first time enumerating this object, or we may have // just reached the end of the current chunk and need to grab another one? In either // case, we will look for more data from the underlying enumerator. Because we // share the same enumerator object, we have to do this under a lock. lock (m_sourceSyncLock) { Contract.Assert(0 <= mutables.m_nextChunkMaxSize && mutables.m_nextChunkMaxSize <= chunkBuffer.Length); // Accumulate a chunk of elements from the input. int i = 0; if (m_exceptionTracker.Value) { return(false); } try { for (; i < mutables.m_nextChunkMaxSize && m_source.MoveNext(); i++) { // Read the current entry into our buffer. chunkBuffer[i] = m_source.Current; } } catch { m_exceptionTracker.Value = true; throw; } // Store the number of elements we fetched from the data source. mutables.m_currentChunkSize = i; // If we've emptied the enumerator, return immediately. if (i == 0) { return(false); } // Increment the shared index for all to see. Throw an exception on overflow. mutables.m_chunkBaseIndex = m_currentIndex.Value; checked { m_currentIndex.Value += i; } } // Each time we access the data source, we grow the chunk size for the next go-round. // We grow the chunksize once per 'chunksPerChunkSize'. if (mutables.m_nextChunkMaxSize < chunkBuffer.Length) { if ((mutables.m_chunkCounter++ & chunksPerChunkSize) == chunksPerChunkSize) { mutables.m_nextChunkMaxSize = mutables.m_nextChunkMaxSize * 2; if (mutables.m_nextChunkMaxSize > chunkBuffer.Length) { mutables.m_nextChunkMaxSize = chunkBuffer.Length; } } } // Finally, reset our index to the beginning; loop around and we'll return the right values. mutables.m_currentChunkIndex = -1; } }
private IReadOnlyDictionary <string, IType> ToProperties() { return(Mutables.ToDictionary(x => x.Name, y => y.Type)); }
private object GetMutableDefault(string property) { return(Mutables.Where(mutable => mutable.Name == property).FirstOrDefault()?.Default); }
//--------------------------------------------------------------------------------------- // Called when this enumerator is first enumerated; it must walk through the source // and redistribute elements to their slot in the exchange matrix. // private void EnumerateAndRedistributeElements() { Mutables mutables = m_mutables; Contract.Assert(mutables != null); ListChunk <Pair <TInputOutput, THashKey> >[] privateBuffers = new ListChunk <Pair <TInputOutput, THashKey> > [m_partitionCount]; ListChunk <TOrderKey>[] privateKeyBuffers = new ListChunk <TOrderKey> [m_partitionCount]; TInputOutput element = default(TInputOutput); TOrderKey key = default(TOrderKey); int loopCount = 0; while (m_source.MoveNext(ref element, ref key)) { if ((loopCount++ & CancellationState.POLL_INTERVAL) == 0) { CancellationState.ThrowIfCanceled(m_cancellationToken); } // Calculate the element's destination partition index, placing it into the // appropriate buffer from which partitions will later enumerate. int destinationIndex; THashKey elementHashKey = default(THashKey); if (m_keySelector != null) { elementHashKey = m_keySelector(element); destinationIndex = m_repartitionStream.GetHashCode(elementHashKey) % m_partitionCount; } else { Contract.Assert(typeof(THashKey) == typeof(NoKeyMemoizationRequired)); destinationIndex = m_repartitionStream.GetHashCode(element) % m_partitionCount; } Contract.Assert(0 <= destinationIndex && destinationIndex < m_partitionCount, "destination partition outside of the legal range of partitions"); // Get the buffer for the destnation partition, lazily allocating if needed. We maintain // this list in our own private cache so that we avoid accessing shared memory locations // too much. In the original implementation, we'd access the buffer in the matrix ([N,M], // where N is the current partition and M is the destination), but some rudimentary // performance profiling indicates copying at the end performs better. ListChunk <Pair <TInputOutput, THashKey> > buffer = privateBuffers[destinationIndex]; ListChunk <TOrderKey> keyBuffer = privateKeyBuffers[destinationIndex]; if (buffer == null) { const int INITIAL_PRIVATE_BUFFER_SIZE = 128; Contract.Assert(keyBuffer == null); privateBuffers[destinationIndex] = buffer = new ListChunk <Pair <TInputOutput, THashKey> >(INITIAL_PRIVATE_BUFFER_SIZE); privateKeyBuffers[destinationIndex] = keyBuffer = new ListChunk <TOrderKey>(INITIAL_PRIVATE_BUFFER_SIZE); } buffer.Add(new Pair <TInputOutput, THashKey>(element, elementHashKey)); keyBuffer.Add(key); } // Copy the local buffers to the shared space and then signal to other threads that // we are done. We can then immediately move on to enumerating the elements we found // for the current partition before waiting at the barrier. If we found a lot, we will // hopefully never have to physically wait. for (int i = 0; i < m_partitionCount; i++) { m_valueExchangeMatrix[m_partitionIndex, i] = privateBuffers[i]; m_keyExchangeMatrix[m_partitionIndex, i] = privateKeyBuffers[i]; } m_barrier.Signal(); // Begin at our own buffer. mutables.m_currentBufferIndex = m_partitionIndex; mutables.m_currentBuffer = privateBuffers[m_partitionIndex]; mutables.m_currentKeyBuffer = privateKeyBuffers[m_partitionIndex]; mutables.m_currentIndex = ENUMERATION_NOT_STARTED; }
//--------------------------------------------------------------------------------------- // Straightforward IEnumerator<T> methods. // internal override bool MoveNext(ref TOutput currentElement, ref Pair <TLeftKey, int> currentKey) { while (true) { if (_currentRightSource == null) { _mutables = new Mutables(); // Check cancellation every few lhs-enumerations in case none of them are producing // any outputs. Otherwise, we rely on the consumer of this operator to be performing the checks. if ((_mutables._lhsCount++ & CancellationState.POLL_INTERVAL) == 0) { CancellationState.ThrowIfCanceled(_cancellationToken); } // We don't have a "current" right enumerator to use. We have to fetch the next // one. If the left has run out of elements, however, we're done and just return // false right away. if (!_leftSource.MoveNext(ref _mutables._currentLeftElement, ref _mutables._currentLeftKey)) { return(false); } // Use the source selection routine to create a right child. IEnumerable <TRightInput> rightChild = _selectManyOperator._rightChildSelector(_mutables._currentLeftElement); Debug.Assert(rightChild != null); _currentRightSource = rightChild.GetEnumerator(); Debug.Assert(_currentRightSource != null); // If we have no result selector, we will need to access the Current element of the right // data source as though it is a TOutput. Unfortunately, we know that TRightInput must // equal TOutput (we check it during operator construction), but the type system doesn't. // Thus we would have to cast the result of invoking Current from type TRightInput to // TOutput. This is no good, since the results could be value types. Instead, we save the // enumerator object as an IEnumerator<TOutput> and access that later on. if (_selectManyOperator._resultSelector == null) { _currentRightSourceAsOutput = (IEnumerator <TOutput>)_currentRightSource; Debug.Assert(_currentRightSourceAsOutput == _currentRightSource, "these must be equal, otherwise the surrounding logic will be broken"); } } if (_currentRightSource.MoveNext()) { _mutables._currentRightSourceIndex++; // If the inner data source has an element, we can yield it. if (_selectManyOperator._resultSelector != null) { // In the case of a selection function, use that to yield the next element. currentElement = _selectManyOperator._resultSelector(_mutables._currentLeftElement, _currentRightSource.Current); } else { // Otherwise, the right input and output types must be the same. We use the // casted copy of the current right source and just return its current element. Debug.Assert(_currentRightSourceAsOutput != null); currentElement = _currentRightSourceAsOutput.Current; } currentKey = new Pair <TLeftKey, int>(_mutables._currentLeftKey, _mutables._currentRightSourceIndex); return(true); } else { // Otherwise, we have exhausted the right data source. Loop back around and try // to get the next left element, then its right, and so on. _currentRightSource.Dispose(); _currentRightSource = null; _currentRightSourceAsOutput = null; } } }
//--------------------------------------------------------------------------------------- // Retrieves the next element from this partition. All repartitioning operators across // all partitions cooperate in a barrier-style algorithm. The first time an element is // requested, the repartitioning operator will enter the 1st phase: during this phase, it // scans its entire input and compute the destination partition for each element. During // the 2nd phase, each partition scans the elements found by all other partitions for // it, and yield this to callers. The only synchronization required is the barrier itself // -- all other parts of this algorithm are synchronization-free. // // Notes: One rather large penalty that this algorithm incurs is higher memory usage and a // larger time-to-first-element latency, at least compared with our old implementation; this // happens because all input elements must be fetched before we can produce a single output // element. In many cases this isn't too terrible: e.g. a GroupBy requires this to occur // anyway, so having the repartitioning operator do so isn't complicating matters much at all. // internal override bool MoveNext(ref Pair <TInputOutput, THashKey> currentElement, ref TOrderKey currentKey) { if (m_partitionCount == 1) { TInputOutput current = default(TInputOutput); // If there's only one partition, no need to do any sort of exchanges. if (m_source.MoveNext(ref current, ref currentKey)) { currentElement = new Pair <TInputOutput, THashKey>( current, m_keySelector == null ? default(THashKey) : m_keySelector(current)); return(true); } return(false); } Mutables mutables = m_mutables; if (mutables == null) { mutables = m_mutables = new Mutables(); } // If we haven't enumerated the source yet, do that now. This is the first phase // of a two-phase barrier style operation. if (mutables.m_currentBufferIndex == ENUMERATION_NOT_STARTED) { EnumerateAndRedistributeElements(); Contract.Assert(mutables.m_currentBufferIndex != ENUMERATION_NOT_STARTED); } // Once we've enumerated our contents, we can then go back and walk the buffers that belong // to the current partition. This is phase two. Note that we slyly move on to the first step // of phase two before actually waiting for other partitions. That's because we can enumerate // the buffer we wrote to above, as already noted. while (mutables.m_currentBufferIndex < m_partitionCount) { // If the queue is non-null and still has elements, yield them. if (mutables.m_currentBuffer != null) { Contract.Assert(mutables.m_currentKeyBuffer != null); if (++mutables.m_currentIndex < mutables.m_currentBuffer.Count) { // Return the current element. currentElement = mutables.m_currentBuffer.m_chunk[mutables.m_currentIndex]; Contract.Assert(mutables.m_currentKeyBuffer != null, "expected same # of buffers/key-buffers"); currentKey = mutables.m_currentKeyBuffer.m_chunk[mutables.m_currentIndex]; return(true); } else { // If the chunk is empty, advance to the next one (if any). mutables.m_currentIndex = ENUMERATION_NOT_STARTED; mutables.m_currentBuffer = mutables.m_currentBuffer.Next; mutables.m_currentKeyBuffer = mutables.m_currentKeyBuffer.Next; Contract.Assert(mutables.m_currentBuffer == null || mutables.m_currentBuffer.Count > 0); Contract.Assert((mutables.m_currentBuffer == null) == (mutables.m_currentKeyBuffer == null)); Contract.Assert(mutables.m_currentBuffer == null || mutables.m_currentBuffer.Count == mutables.m_currentKeyBuffer.Count); continue; // Go back around and invoke this same logic. } } // We're done with the current partition. Slightly different logic depending on whether // we're on our own buffer or one that somebody else found for us. if (mutables.m_currentBufferIndex == m_partitionIndex) { // We now need to wait at the barrier, in case some other threads aren't done. // Once we wake up, we reset our index and will increment it immediately after. m_barrier.Wait(m_cancellationToken); mutables.m_currentBufferIndex = ENUMERATION_NOT_STARTED; } // Advance to the next buffer. mutables.m_currentBufferIndex++; mutables.m_currentIndex = ENUMERATION_NOT_STARTED; if (mutables.m_currentBufferIndex == m_partitionIndex) { // Skip our current buffer (since we already enumerated it). mutables.m_currentBufferIndex++; } // Assuming we're within bounds, retrieve the next buffer object. if (mutables.m_currentBufferIndex < m_partitionCount) { mutables.m_currentBuffer = m_valueExchangeMatrix[mutables.m_currentBufferIndex, m_partitionIndex]; mutables.m_currentKeyBuffer = m_keyExchangeMatrix[mutables.m_currentBufferIndex, m_partitionIndex]; } } // We're done. No more buffers to enumerate. return(false); }
internal override bool MoveNext(ref TOutput currentElement, ref TLeftKey currentKey) { Mutables <TLeftInput, TLeftKey, TRightInput, THashKey, TOutput> mutables = this.m_mutables; if (mutables == null) { mutables = this.m_mutables = new Mutables <TLeftInput, TLeftKey, TRightInput, THashKey, TOutput>(); mutables.m_rightHashLookup = new HashLookup <THashKey, Pair <TRightInput, ListChunk <TRightInput> > >(this.m_keyComparer); Pair <TRightInput, THashKey> pair = new Pair <TRightInput, THashKey>(); int num = 0; int num2 = 0; while (this.m_rightSource.MoveNext(ref pair, ref num)) { if ((num2++ & 0x3f) == 0) { CancellationState.ThrowIfCanceled(this.m_cancellationToken); } TRightInput first = pair.First; THashKey second = pair.Second; if (second != null) { Pair <TRightInput, ListChunk <TRightInput> > pair2 = new Pair <TRightInput, ListChunk <TRightInput> >(); if (!mutables.m_rightHashLookup.TryGetValue(second, ref pair2)) { pair2 = new Pair <TRightInput, ListChunk <TRightInput> >(first, null); if (this.m_groupResultSelector != null) { pair2.Second = new ListChunk <TRightInput>(2); pair2.Second.Add(first); } mutables.m_rightHashLookup.Add(second, pair2); } else { if (pair2.Second == null) { pair2.Second = new ListChunk <TRightInput>(2); mutables.m_rightHashLookup[second] = pair2; } pair2.Second.Add(first); } } } } ListChunk <TRightInput> currentRightMatches = mutables.m_currentRightMatches; if ((currentRightMatches != null) && (mutables.m_currentRightMatchesIndex == currentRightMatches.Count)) { currentRightMatches = mutables.m_currentRightMatches = currentRightMatches.Next; mutables.m_currentRightMatchesIndex = 0; } if (mutables.m_currentRightMatches == null) { Pair <TLeftInput, THashKey> pair3 = new Pair <TLeftInput, THashKey>(); TLeftKey local3 = default(TLeftKey); while (this.m_leftSource.MoveNext(ref pair3, ref local3)) { if ((mutables.m_outputLoopCount++ & 0x3f) == 0) { CancellationState.ThrowIfCanceled(this.m_cancellationToken); } Pair <TRightInput, ListChunk <TRightInput> > pair4 = new Pair <TRightInput, ListChunk <TRightInput> >(); TLeftInput local4 = pair3.First; THashKey key = pair3.Second; if (((key != null) && mutables.m_rightHashLookup.TryGetValue(key, ref pair4)) && (this.m_singleResultSelector != null)) { mutables.m_currentRightMatches = pair4.Second; mutables.m_currentRightMatchesIndex = 0; currentElement = this.m_singleResultSelector(local4, pair4.First); currentKey = local3; if (pair4.Second != null) { mutables.m_currentLeft = local4; mutables.m_currentLeftKey = local3; } return(true); } if (this.m_groupResultSelector != null) { IEnumerable <TRightInput> enumerable = pair4.Second; if (enumerable == null) { enumerable = (IEnumerable <TRightInput>)ParallelEnumerable.Empty <TRightInput>(); } currentElement = this.m_groupResultSelector(local4, enumerable); currentKey = local3; return(true); } } return(false); } currentElement = this.m_singleResultSelector(mutables.m_currentLeft, mutables.m_currentRightMatches.m_chunk[mutables.m_currentRightMatchesIndex]); currentKey = mutables.m_currentLeftKey; mutables.m_currentRightMatchesIndex++; return(true); }
public IMutableElement GetMutableAction(Type type) { return Mutables.ContainsKey(type) ? Mutables[type] : null; }
//--------------------------------------------------------------------------------------- // MoveNext implements all the hash-join logic noted earlier. When it is called first, it // will execute the entire inner query tree, and build a hash-table lookup. This is the // Building phase. Then for the first call and all subsequent calls to MoveNext, we will // incrementally perform the Probing phase. We'll keep getting elements from the outer // data source, looking into the hash-table we built, and enumerating the full results. // // This routine supports both inner and outer (group) joins. An outer join will yield a // (possibly empty) list of matching elements from the inner instead of one-at-a-time, // as we do for inner joins. // internal override bool MoveNext(ref TOutput currentElement, ref TLeftKey currentKey) { Contract.Assert(_singleResultSelector != null || _groupResultSelector != null, "expected a compiled result selector"); Contract.Assert(_leftSource != null); Contract.Assert(_rightSource != null); // BUILD phase: If we haven't built the hash-table yet, create that first. Mutables mutables = _mutables; if (mutables == null) { mutables = _mutables = new Mutables(); #if DEBUG int hashLookupCount = 0; int hashKeyCollisions = 0; #endif mutables._rightHashLookup = new HashLookup <THashKey, Pair>(_keyComparer); Pair rightPair = new Pair(default(TRightInput), default(THashKey)); int rightKeyUnused = default(int); int i = 0; while (_rightSource.MoveNext(ref rightPair, ref rightKeyUnused)) { if ((i++ & CancellationState.POLL_INTERVAL) == 0) { CancellationState.ThrowIfCanceled(_cancellationToken); } TRightInput rightElement = (TRightInput)rightPair.First; THashKey rightHashKey = (THashKey)rightPair.Second; // We ignore null keys. if (rightHashKey != null) { #if DEBUG hashLookupCount++; #endif // See if we've already stored an element under the current key. If not, we // lazily allocate a pair to hold the elements mapping to the same key. const int INITIAL_CHUNK_SIZE = 2; Pair currentValue = new Pair(default(TRightInput), default(ListChunk <TRightInput>)); if (!mutables._rightHashLookup.TryGetValue(rightHashKey, ref currentValue)) { currentValue = new Pair(rightElement, null); if (_groupResultSelector != null) { // For group joins, we also add the element to the list. This makes // it easier later to yield the list as-is. currentValue.Second = new ListChunk <TRightInput>(INITIAL_CHUNK_SIZE); ((ListChunk <TRightInput>)currentValue.Second).Add((TRightInput)rightElement); } mutables._rightHashLookup.Add(rightHashKey, currentValue); } else { if (currentValue.Second == null) { // Lazily allocate a list to hold all but the 1st value. We need to // re-store this element because the pair is a value type. currentValue.Second = new ListChunk <TRightInput>(INITIAL_CHUNK_SIZE); mutables._rightHashLookup[rightHashKey] = currentValue; } ((ListChunk <TRightInput>)currentValue.Second).Add((TRightInput)rightElement); #if DEBUG hashKeyCollisions++; #endif } } } #if DEBUG TraceHelpers.TraceInfo("ParallelJoinQueryOperator::MoveNext - built hash table [count = {0}, collisions = {1}]", hashLookupCount, hashKeyCollisions); #endif } // PROBE phase: So long as the source has a next element, return the match. ListChunk <TRightInput> currentRightChunk = mutables._currentRightMatches; if (currentRightChunk != null && mutables._currentRightMatchesIndex == currentRightChunk.Count) { currentRightChunk = mutables._currentRightMatches = currentRightChunk.Next; mutables._currentRightMatchesIndex = 0; } if (mutables._currentRightMatches == null) { // We have to look up the next list of matches in the hash-table. Pair leftPair = new Pair(default(TLeftInput), default(THashKey)); TLeftKey leftKey = default(TLeftKey); while (_leftSource.MoveNext(ref leftPair, ref leftKey)) { if ((mutables._outputLoopCount++ & CancellationState.POLL_INTERVAL) == 0) { CancellationState.ThrowIfCanceled(_cancellationToken); } // Find the match in the hash table. Pair matchValue = new Pair(default(TRightInput), default(ListChunk <TRightInput>)); TLeftInput leftElement = (TLeftInput)leftPair.First; THashKey leftHashKey = (THashKey)leftPair.Second; // Ignore null keys. if (leftHashKey != null) { if (mutables._rightHashLookup.TryGetValue(leftHashKey, ref matchValue)) { // We found a new match. For inner joins, we remember the list in case // there are multiple value under this same key -- the next iteration will pick // them up. For outer joins, we will use the list momentarily. if (_singleResultSelector != null) { mutables._currentRightMatches = (ListChunk <TRightInput>)matchValue.Second; Contract.Assert(mutables._currentRightMatches == null || mutables._currentRightMatches.Count > 0, "we were expecting that the list would be either null or empty"); mutables._currentRightMatchesIndex = 0; // Yield the value. currentElement = _singleResultSelector(leftElement, (TRightInput)matchValue.First); currentKey = leftKey; // If there is a list of matches, remember the left values for next time. if (matchValue.Second != null) { mutables._currentLeft = leftElement; mutables._currentLeftKey = leftKey; } return(true); } } } // For outer joins, we always yield a result. if (_groupResultSelector != null) { // Grab the matches, or create an empty list if there are none. IEnumerable <TRightInput> matches = (ListChunk <TRightInput>)matchValue.Second; if (matches == null) { matches = ParallelEnumerable.Empty <TRightInput>(); } // Generate the current value. currentElement = _groupResultSelector(leftElement, matches); currentKey = leftKey; return(true); } } // If we've reached the end of the data source, we're done. return(false); } // Produce the next element and increment our index within the matches. Contract.Assert(_singleResultSelector != null); Contract.Assert(mutables._currentRightMatches != null); Contract.Assert(0 <= mutables._currentRightMatchesIndex && mutables._currentRightMatchesIndex < mutables._currentRightMatches.Count); currentElement = _singleResultSelector( mutables._currentLeft, mutables._currentRightMatches._chunk[mutables._currentRightMatchesIndex]); currentKey = mutables._currentLeftKey; mutables._currentRightMatchesIndex++; return(true); }
//--------------------------------------------------------------------------------------- // MoveNext implements all the hash-join logic noted earlier. When it is called first, it // will execute the entire inner query tree, and build a hash-table lookup. This is the // Building phase. Then for the first call and all subsequent calls to MoveNext, we will // incrementally perform the Probing phase. We'll keep getting elements from the outer // data source, looking into the hash-table we built, and enumerating the full results. // // This routine supports both inner and outer (group) joins. An outer join will yield a // (possibly empty) list of matching elements from the inner instead of one-at-a-time, // as we do for inner joins. // internal override bool MoveNext(ref TOutput currentElement, ref TOutputKey currentKey) { Debug.Assert(_resultSelector != null, "expected a compiled result selector"); Debug.Assert(_leftSource != null); Debug.Assert(_rightLookupBuilder != null); // BUILD phase: If we haven't built the hash-table yet, create that first. Mutables mutables = _mutables; if (mutables == null) { mutables = _mutables = new Mutables(); mutables._rightHashLookup = _rightLookupBuilder.BuildHashLookup(_cancellationToken); } // PROBE phase: So long as the source has a next element, return the match. ListChunk <Pair <TRightInput, TRightKey> > currentRightChunk = mutables._currentRightMatches; if (currentRightChunk != null && mutables._currentRightMatchesIndex == currentRightChunk.Count) { mutables._currentRightMatches = currentRightChunk.Next; mutables._currentRightMatchesIndex = 0; } if (mutables._currentRightMatches == null) { // We have to look up the next list of matches in the hash-table. Pair <TLeftInput, THashKey> leftPair = default(Pair <TLeftInput, THashKey>); TLeftKey leftKey = default(TLeftKey); while (_leftSource.MoveNext(ref leftPair, ref leftKey)) { if ((mutables._outputLoopCount++ & CancellationState.POLL_INTERVAL) == 0) { CancellationState.ThrowIfCanceled(_cancellationToken); } // Find the match in the hash table. HashLookupValueList <TRightInput, TRightKey> matchValue = default(HashLookupValueList <TRightInput, TRightKey>); TLeftInput leftElement = leftPair.First; THashKey leftHashKey = leftPair.Second; // Ignore null keys. if (leftHashKey != null) { if (mutables._rightHashLookup.TryGetValue(leftHashKey, ref matchValue)) { // We found a new match. We remember the list in case there are multiple // values under this same key -- the next iteration will pick them up. mutables._currentRightMatches = matchValue.Tail; Debug.Assert(mutables._currentRightMatches == null || mutables._currentRightMatches.Count > 0, "we were expecting that the list would be either null or empty"); mutables._currentRightMatchesIndex = 0; // Yield the value. currentElement = _resultSelector(leftElement, matchValue.Head.First); currentKey = _outputKeyBuilder.Combine(leftKey, matchValue.Head.Second); // If there is a list of matches, remember the left values for next time. if (matchValue.Tail != null) { mutables._currentLeft = leftElement; mutables._currentLeftKey = leftKey; } return(true); } } } // If we've reached the end of the data source, we're done. return(false); } // Produce the next element. Debug.Assert(mutables._currentRightMatches != null); Debug.Assert(0 <= mutables._currentRightMatchesIndex && mutables._currentRightMatchesIndex < mutables._currentRightMatches.Count); Pair <TRightInput, TRightKey> rightMatch = mutables._currentRightMatches._chunk[mutables._currentRightMatchesIndex]; currentElement = _resultSelector(mutables._currentLeft, rightMatch.First); currentKey = _outputKeyBuilder.Combine(mutables._currentLeftKey, rightMatch.Second); mutables._currentRightMatchesIndex++; return(true); }