/// <summary> /// Adds the <paramref name="state" /> to the cache if it is not already known. Returns <c>true</c> to indicate that the state /// has been added. This method can be called simultaneously from multiple threads. /// </summary> /// <param name="state">The state that should be added.</param> /// <param name="index">Returns the unique index of the state.</param> public override bool AddState(byte *state, out int index) { // We don't have to do any out of bounds checks here var hash = MemoryBuffer.Hash(state, _stateVectorSize, 0); for (var i = 1; i < ProbeThreshold; ++i) { // We store 30 bit hash values as 32 bit integers, with the most significant bit #31 being set // indicating the 'written' state and bit #30 indicating whether writing is not yet finished // 'empty' is represented by 0 // We ignore two most significant bits of the original hash, which has no influence on the // correctness of the algorithm, but might result in more state comparisons var hashedIndex = MemoryBuffer.Hash((byte *)&hash, sizeof(int), i * 8345723) % _cachedStatesCapacity; var memoizedHash = hashedIndex & 0x3FFFFFFF; var cacheLineStart = (hashedIndex / BucketsPerCacheLine) * BucketsPerCacheLine; for (var j = 0; j < BucketsPerCacheLine; ++j) { var offset = (int)(cacheLineStart + (hashedIndex + j) % BucketsPerCacheLine); var currentValue = Volatile.Read(ref _hashMemory[offset]); if (currentValue == 0 && Interlocked.CompareExchange(ref _hashMemory[offset], (int)memoizedHash | (1 << 30), 0) == 0) { var freshCompactIndex = InterlockedExtensions.IncrementReturnOld(ref _savedStates); Volatile.Write(ref _indexMapperMemory[offset], freshCompactIndex); MemoryBuffer.Copy(state, this[freshCompactIndex], _stateVectorSize); Volatile.Write(ref _hashMemory[offset], (int)memoizedHash | (1 << 31)); index = freshCompactIndex; return(true); } // We have to read the hash value again as it might have been written now where it previously was not currentValue = Volatile.Read(ref _hashMemory[offset]); if ((currentValue & 0x3FFFFFFF) == memoizedHash) { while ((currentValue & 1 << 31) == 0) { currentValue = Volatile.Read(ref _hashMemory[offset]); } var compactIndex = Volatile.Read(ref _indexMapperMemory[offset]); if (compactIndex != -1 && MemoryBuffer.AreEqual(state, this[compactIndex], _stateVectorSize)) { index = compactIndex; return(false); } } } } throw new OutOfMemoryException( "Failed to find an empty hash table slot within a reasonable amount of time. Try increasing the state capacity."); }
private long GetPlaceForNewTransitionTargetElement() { var locationOfNewEntry = InterlockedExtensions.IncrementReturnOld(ref _transitionTargetCount); if (locationOfNewEntry >= _maxNumberOfTransitionTargets) { throw new OutOfMemoryException($"Unable to transition target (limit is {_maxNumberOfTransitionTargets} entries)"); } return(locationOfNewEntry); }
private int GetPlaceForNewTransitionTargetElement() { var locationOfNewEntry = InterlockedExtensions.IncrementReturnOld(ref _transitionTargetCount); if (locationOfNewEntry >= _maxNumberOfTransitionTargets) { throw new OutOfMemoryException("Unable to store distribution."); } return(locationOfNewEntry); }
private int GetPlaceForNewTransitionChainElement() { var locationOfNewEntry = InterlockedExtensions.IncrementReturnOld(ref _transitionChainElementCount); if (locationOfNewEntry >= _maxNumberOfTransitions) { throw new OutOfMemoryException("Unable to store transitions. Try increasing the transition capacity."); } return(locationOfNewEntry); }
/// <summary> /// Reserve a state index in StateStorage. Must not be called after AddState has been called. /// </summary> internal override int ReserveStateIndex() { var freshCompactIndex = InterlockedExtensions.IncrementReturnOld(ref _savedStates); // Use the index pointing at the last possible element in the buffers and decrease the size. _reservedStatesCapacity++; _cachedStatesCapacity--; // Add BucketsPerCacheLine so returnIndex does not interfere with the maximal possible index returned by AddState // which is _cachedStatesCapacity+BucketsPerCacheLine-1. // returnIndex is in range of capacityToReserve, so this is save. var hashBasedIndex = _cachedStatesCapacity + BucketsPerCacheLine; Volatile.Write(ref _indexMapperMemory[hashBasedIndex], freshCompactIndex); Assert.InRange(hashBasedIndex, 0, Int32.MaxValue); Assert.InRange(hashBasedIndex, 0, _totalCapacity + BucketsPerCacheLine); return((int)freshCompactIndex); }