Skip to content

Instantly share code, notes, and snippets.

@msedi
Created August 19, 2021 13:50
Show Gist options
  • Save msedi/f3ab7e91b197a74a7a654ca28471ac0f to your computer and use it in GitHub Desktop.
Save msedi/f3ab7e91b197a74a7a654ca28471ac0f to your computer and use it in GitHub Desktop.
MemoryMappedFile
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);
}
}
}
}
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net5.0</TargetFramework>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
<OutputType>Exe</OutputType>
<PlatformTarget>x64</PlatformTarget>
</PropertyGroup>
</Project>
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;
}
}
}
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;
}
}
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