public DirectBuffer this[BufferRef bufferRef] { [MethodImpl(MethodImplOptions.AggressiveInlining)] get { var bucketIndex = bufferRef.BucketIndex; var bufferIndex = bufferRef.BufferIndex - 1; // BufferRef is one-based if (bucketIndex > _maxBucketIndex) { ThrowBucketIndexAboveMax(); } SharedMemoryBucket[] bucketFiles = _buckets[bucketIndex]; var bufferFileIndex = bufferIndex >> (BufferRef.MaxBucketIdx - bucketIndex); var bufferIndexInFile = bufferIndex & ((1 << (BufferRef.MaxBucketIdx - bucketIndex)) - 1); SharedMemoryBucket bucket = default; if (bucketFiles != null && bufferFileIndex < bucketFiles.Length) { bucket = bucketFiles[bufferFileIndex]; } if (bucket.DirectFile == null) { bucket = CreateBucket(ref bucketFiles, bufferRef, bufferFileIndex, bucketIndex); } return(bucket[bufferIndexInFile]); } }
public DirectBuffer DangerousGet(BufferRef bufferRef) { var bucketIndex = bufferRef.BucketIndex; var bufferIndex = bufferRef.BufferIndex - 1; // BufferRef is one-based SharedMemoryBucket[] bucketFiles = _buckets[bucketIndex]; var bufferFileIndex = bufferIndex >> (BufferRef.MaxBucketIdx - bucketIndex); var bufferIndexInFile = bufferIndex & ((1 << (BufferRef.MaxBucketIdx - bucketIndex)) - 1); SharedMemoryBucket bucket = default; if (bucketFiles != null) { // we need < cmp for not throwing (and it could be <), so add >= 0 // in a *hope* that compiler then eliminates bound check and // we have 2 cmp instead of 3. Benchmark inconclusive, // and this not that important to spend time on disassembling. if (bufferFileIndex >= 0 && bufferFileIndex < bucketFiles.Length) { bucket = bucketFiles[bufferFileIndex]; } } if (bucket.DirectFile == null) { bucket = CreateBucket(ref bucketFiles, bufferRef, bufferFileIndex, bucketIndex); } return(bucket.DangerousGet(bufferIndexInFile)); }
private SharedMemoryBucket CreateBucket(ref SharedMemoryBucket[] bucketFiles, BufferRef bufferRef, int bufferFileIndex, int bucketIndex) { SharedMemoryBucket bucket; lock (_buckets) { if (bucketFiles == null) { bucketFiles = new SharedMemoryBucket[4]; _buckets[bufferRef.BucketIndex] = bucketFiles; } if (bufferFileIndex >= bucketFiles.Length) { var newSize = BitUtil.FindNextPositivePowerOfTwo(bufferFileIndex + 1); var newbucketFiles = new SharedMemoryBucket[newSize]; Array.Copy(bucketFiles, 0, newbucketFiles, 0, bucketFiles.Length); bucketFiles = newbucketFiles; _buckets[bufferRef.BucketIndex] = newbucketFiles; } bucket = bucketFiles[bufferFileIndex]; if (bucket.DirectFile == null) { var bucketDir = Path.Combine(_directoryPath, bucketIndex.ToString()); Directory.CreateDirectory(bucketDir); var buffersPerBucketFile = (1 << 15) >> bucketIndex; var bufferSize = SharedMemoryPool.BucketIndexToBufferSize(bucketIndex, _pageSize); var allocationSize = buffersPerBucketFile * bufferSize; // Since we are using sparse files check available free space var available = GetAvailableFreeSpace(); // 2x just in case, maybe we need to monitor this and stop any allocations everywhere when below c.200MB or 1GB to be sure if (available >= 0 && allocationSize * 2 > available) { throw new NotEnoughSpaceException(true); } // Console.WriteLine($"allocationSize for bucket {bucketIndex}: {allocationSize}"); var df = new DirectFile(Path.Combine(bucketDir, bufferFileIndex + ".smbkt"), allocationSize, true, FileOptions.RandomAccess // Note that we cannot use async with sparse without using valid NativeOverlapped struct in DeviceIoControl function | FileOptions.WriteThrough, // TODO review if we need this true); DirectFile.PrefetchMemory(df.DirectBuffer); Trace.TraceInformation($"Allocated new file in a bucket {bucketIndex}. Total # of files: {bucketFiles.Count(x => x.DirectFile != null)}"); bucket = new SharedMemoryBucket(df, bucketIndex, _pageSize); // Console.WriteLine($"File capacity for bucket {bucketIndex}: {bucket.BufferCount}"); bucketFiles[bufferFileIndex] = bucket; } } return(bucket); }