Created
August 19, 2021 13:50
-
-
Save msedi/f3ab7e91b197a74a7a654ca28471ac0f to your computer and use it in GitHub Desktop.
MemoryMappedFile
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
using System; | |
using System.IO; | |
using System.Runtime.InteropServices; | |
namespace MemoryMapFile | |
{ | |
/// <summary> | |
/// Base class for Volumes and Processors | |
/// </summary> | |
abstract class DataSource | |
{ | |
public int SizeX; | |
public int SizeY; | |
public int SizeZ; | |
public DataSource() | |
{ | |
} | |
abstract public ReadOnlySpan<int> GetVolume(int sliceIndex); | |
public void SaveToFile(string fileName) | |
{ | |
using var stream = File.Create(fileName); | |
stream.SetLength((long)SizeX * SizeY * SizeZ * (long)sizeof(int)); | |
for (int z = 0; z < SizeZ; z++) | |
{ | |
var span = GetVolume(z); | |
var spantowrite = MemoryMarshal.Cast<int, byte>(span); | |
stream.Write(spantowrite); | |
} | |
} | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<Project Sdk="Microsoft.NET.Sdk"> | |
<PropertyGroup> | |
<TargetFramework>net5.0</TargetFramework> | |
<AllowUnsafeBlocks>true</AllowUnsafeBlocks> | |
<OutputType>Exe</OutputType> | |
<PlatformTarget>x64</PlatformTarget> | |
</PropertyGroup> | |
</Project> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
using System; | |
using System.Buffers; | |
namespace MemoryMapFile | |
{ | |
/// <summary> | |
/// Processor that takes an input and produces an output. | |
/// </summary> | |
unsafe class Processor: DataSource | |
{ | |
readonly DataSource InputVolume; | |
readonly Volume OutputVolume; | |
public Processor(DataSource inputVolume) | |
{ | |
InputVolume = inputVolume; | |
OutputVolume = new Volume(inputVolume.SizeX, inputVolume.SizeY, inputVolume.SizeZ); | |
SizeX = inputVolume.SizeX; | |
SizeY = inputVolume.SizeY; | |
SizeZ = inputVolume.SizeZ; | |
} | |
/// <summary> | |
/// The processor just adds 10 to the original data. | |
/// </summary> | |
/// <param name="sliceIndex"></param> | |
/// <returns></returns> | |
public override ReadOnlySpan<int> GetVolume(int sliceIndex) | |
{ | |
Span<int> data = GC.AllocateUninitializedArray<int>(SizeX*SizeY); | |
var inputData = InputVolume.GetVolume(sliceIndex); | |
for (int i = 0; i < data.Length; i++) | |
{ | |
data[i] = inputData[i] + 10; | |
} | |
OutputVolume.SetVolume(sliceIndex, data); | |
return data; | |
} | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
using System; | |
using System.Collections.Generic; | |
using System.ComponentModel; | |
using System.Diagnostics; | |
using System.Runtime.InteropServices; | |
using System.Security; | |
using System.Threading; | |
using System.Threading.Tasks; | |
namespace MemoryMapFile | |
{ | |
class Program | |
{ | |
static void Main(string[] args) | |
{ | |
Stopwatch stopWatch = new(); | |
stopWatch.Start(); | |
// Create the root volume. | |
DataSource volumeStart = new Volume(2048, 2048, 20000);//, "C:\\MMF\\SourceVolume.tmp"); | |
// Create the processor chain | |
for (int i = 0; i < 1; i++) | |
{ | |
Processor p = new(volumeStart); | |
volumeStart = p; | |
} | |
#region Timing Measurement | |
int currentCount = 0; | |
int lastCount = 0; | |
DateTime lastTime = DateTime.Now; | |
Timer timer = new Timer(callback); | |
void callback(object state) | |
{ | |
if (lastCount == currentCount) | |
return; | |
int nowCount = currentCount; | |
var nowTime = DateTime.Now; | |
int diff = nowCount - lastCount; | |
TimeSpan diffTime = nowTime - lastTime; | |
lastCount = currentCount; | |
lastTime = nowTime; | |
Console.WriteLine($"{nowCount} - Items per s: {diff / diffTime.TotalSeconds} ({GetMemory().ullAvailPhys})"); | |
} | |
timer.Change(0, 1000); | |
#endregion | |
// Process the data. | |
Parallel.For(0, volumeStart.SizeZ, i => | |
//for (int i = 0; i < volumeStart.SizeZ; i++) | |
{ | |
var data = volumeStart.GetVolume(i); | |
Interlocked.Increment(ref currentCount); | |
}); | |
stopWatch.Stop(); | |
TimeSpan ts = stopWatch.Elapsed; | |
Console.WriteLine("RunTime: " + ts.TotalMilliseconds + "ms"); | |
} | |
private static readonly uint SizeOfMemStat = (uint)Marshal.SizeOf(typeof(MEMORYSTATUSEX)); | |
public static MEMORYSTATUSEX GetMemory() | |
{ | |
MEMORYSTATUSEX memstat = new() | |
{ | |
dwLength = SizeOfMemStat | |
}; | |
if (!GlobalMemoryStatusEx(ref memstat)) | |
throw new Win32Exception(Marshal.GetHRForLastWin32Error()); | |
return memstat; | |
} | |
private static readonly List<byte[]> AllocatedMemory = new(); | |
[SuppressUnmanagedCodeSecurity] | |
[DllImport("kernel32")] static extern bool GlobalMemoryStatusEx(ref MEMORYSTATUSEX lpBuffer); | |
} | |
public struct MEMORYSTATUSEX | |
{ | |
public uint dwLength; | |
public uint dwMemoryLoad; | |
public ulong ullTotalPhys; | |
public ulong ullAvailPhys; | |
public ulong ullTotalPageFile; | |
public ulong ullwAvailPageFile; | |
public ulong ullTotalVirtual; | |
public ulong ullAvailVirtual; | |
public ulong ullAvailExtendedVirtual; | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
using System; | |
using System.IO; | |
using System.IO.MemoryMappedFiles; | |
namespace MemoryMapFile | |
{ | |
unsafe class Volume : DataSource, IDisposable | |
{ | |
readonly MemoryMappedFile mmf; | |
readonly MemoryMappedViewAccessor va; | |
readonly FileStream stream; | |
readonly nint StartOffset; | |
readonly int N; | |
readonly long FileSize; | |
string TempPath = "E:\\MMF"; | |
public Volume(int sizex, int sizey, int sizez, string fileName = null) | |
{ | |
SizeX = sizex; | |
SizeY = sizey; | |
SizeZ = sizez; | |
N = SizeX * SizeY; | |
FileSize = (long)SizeX * SizeY * SizeZ * sizeof(int); | |
if (!Directory.Exists(TempPath)) | |
Directory.CreateDirectory(TempPath); | |
if (string.IsNullOrEmpty(fileName)) | |
{ | |
fileName = Path.Combine(TempPath, Guid.NewGuid().ToString()) + ".tmp"; | |
stream = new FileStream(fileName, FileMode.Create, FileAccess.ReadWrite, FileShare.ReadWrite, 4096, FileOptions.DeleteOnClose | FileOptions.Asynchronous | FileOptions.RandomAccess); | |
} | |
else | |
{ | |
stream = new FileStream(fileName, FileMode.Open, FileAccess.ReadWrite, FileShare.ReadWrite, 4096, FileOptions.Asynchronous | FileOptions.RandomAccess); | |
} | |
mmf = MemoryMappedFile.CreateFromFile(stream, null, FileSize, MemoryMappedFileAccess.ReadWrite, HandleInheritability.None, false); | |
va = mmf.CreateViewAccessor(); | |
bool success = false; | |
va.SafeMemoryMappedViewHandle.DangerousAddRef(ref success); | |
StartOffset = va.SafeMemoryMappedViewHandle.DangerousGetHandle(); | |
} | |
public void Dispose() | |
{ | |
va.SafeMemoryMappedViewHandle.DangerousRelease(); | |
va.Dispose(); | |
stream.Dispose(); | |
mmf.Dispose(); | |
} | |
public override unsafe ReadOnlySpan<int> GetVolume(int sliceIndex) | |
{ | |
long Offset = (long)sliceIndex * N; | |
return new ReadOnlySpan<int>((int*)(StartOffset + Offset), N); | |
} | |
public unsafe void SetVolume(int sliceIndex, ReadOnlySpan<int> data) | |
{ | |
long Offset = (long)sliceIndex * N; | |
var span = new Span<int>((int*)(StartOffset + Offset), N); | |
data.CopyTo(span); | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment