Ejemplo n.º 1
0
        /// <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.");
        }
Ejemplo n.º 2
0
        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);
        }
Ejemplo n.º 3
0
        private int GetPlaceForNewTransitionTargetElement()
        {
            var locationOfNewEntry = InterlockedExtensions.IncrementReturnOld(ref _transitionTargetCount);

            if (locationOfNewEntry >= _maxNumberOfTransitionTargets)
            {
                throw new OutOfMemoryException("Unable to store distribution.");
            }
            return(locationOfNewEntry);
        }
Ejemplo n.º 4
0
        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);
        }
Ejemplo n.º 5
0
        /// <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);
        }