J'ai été impliqué dans l'une des autres questions que vous avez posées, et je pense honnêtement que vous êtes en train de livrer une bataille perdue ici. Vous devez éventuellement explorer d'autres voies de traitement de ces données autres que la lecture de tout en mémoire. Si je comprends bien, vous avez plusieurs threads qui traitent les données simultanément et c'est pourquoi vous ne voulez pas travailler directement sur le fichier en raison de la contention des E/S que je suppose.
Avez-vous envisagé ou auriez-vous la possibilité de lire un bloc de données en mémoire, les threads traitent-ils le bloc, puis le bloc ou le traitement suivant par les threads? De cette façon, à tout moment, vous n'avez jamais plus d'un bloc en mémoire, mais tous les threads peuvent accéder au bloc. Ce n'est pas optimal, mais je l'ai mis là-bas comme point de départ. Si cela est faisable, alors des options pour optimiser ceci peuvent être explorées.
Mise à jour: Exemple d'utilisation de plate-forme pour Invoke allouer de la mémoire non géré et de l'utiliser de .NET.
Puisque vous êtes si certain que vous avez besoin de charger autant de données en mémoire, j'ai pensé écrire une petite application de test pour vérifier que cela fonctionnera.Pour cela, vous aurez besoin de
- Compile avec l'option de compilation
/unsafe
- Si vous souhaitez allouer plus de 2 Go, vous aurez également besoin de changer votre plate-forme cible à x64
* Le point 2 ci-dessus est un peu plus compliqué, sur un système d'exploitation 64 bits, vous pouvez toujours cibler la plate-forme x86
et avoir accès à la mémoire complète 4 GB
. Cela vous obligera à utiliser un outil comme EDITBIN.EXE pour définir l'indicateur LargeAddressAware
dans l'en-tête PE.
Ce code utilise VirtualAllocEx pour allouer de la mémoire non gérée et UnmanagedMemoryStream pour accéder à la mémoire non gérée à l'aide de la métaphore de flux .NET. Notez que ce code n'a fait que quelques tests rapides très basiques et uniquement sur l'environnement 64 bits cible avec 4 Go de RAM . Et le plus important, je suis seulement allé à environ 2.6 Go d'utilisation de la mémoire pour le processus.
using System;
using System.IO;
using System.Runtime.InteropServices;
using System.Diagnostics;
using System.ComponentModel;
namespace MemoryMappedFileTests
{
class Program
{
static void Main(string[] args)
{
IntPtr ptr = IntPtr.Zero;
try
{
// Allocate and Commit the memory directly.
ptr = VirtualAllocEx(
Process.GetCurrentProcess().Handle,
IntPtr.Zero,
new IntPtr(0xD0000000L),
AllocationType.Commit | AllocationType.Reserve,
MemoryProtection.ReadWrite);
if (ptr == IntPtr.Zero)
{
throw new Win32Exception(Marshal.GetLastWin32Error());
}
// Query some information about the allocation, used for testing.
MEMORY_BASIC_INFORMATION mbi = new MEMORY_BASIC_INFORMATION();
IntPtr result = VirtualQueryEx(
Process.GetCurrentProcess().Handle,
ptr,
out mbi,
new IntPtr(Marshal.SizeOf(mbi)));
if (result == IntPtr.Zero)
{
throw new Win32Exception(Marshal.GetLastWin32Error());
}
// Use unsafe code to get a pointer to the unmanaged memory.
// This requires compiling with /unsafe option.
unsafe
{
// Pointer to the allocated memory
byte* pBytes = (byte*)ptr.ToPointer();
// Create Read/Write stream to access the memory.
UnmanagedMemoryStream stm = new UnmanagedMemoryStream(
pBytes,
mbi.RegionSize.ToInt64(),
mbi.RegionSize.ToInt64(),
FileAccess.ReadWrite);
// Create a StreamWriter to write to the unmanaged memory.
StreamWriter sw = new StreamWriter(stm);
sw.Write("Everything seems to be working!\r\n");
sw.Flush();
// Reset the stream position and create a reader to check that the
// data was written correctly.
stm.Position = 0;
StreamReader rd = new StreamReader(stm);
Console.WriteLine(rd.ReadLine());
}
}
catch (Exception ex)
{
Console.WriteLine(ex.ToString());
}
finally
{
if (ptr != IntPtr.Zero)
{
VirtualFreeEx(
Process.GetCurrentProcess().Handle,
ptr,
IntPtr.Zero,
FreeType.Release);
}
}
Console.ReadKey();
}
[DllImport("kernel32.dll", SetLastError = true, ExactSpelling = true)]
static extern IntPtr VirtualAllocEx(
IntPtr hProcess,
IntPtr lpAddress,
IntPtr dwSize,
AllocationType dwAllocationType,
MemoryProtection flProtect);
[DllImport("kernel32.dll", SetLastError = true, ExactSpelling = true)]
static extern bool VirtualFreeEx(
IntPtr hProcess,
IntPtr lpAddress,
IntPtr dwSize,
FreeType dwFreeType);
[DllImport("kernel32.dll", SetLastError = true, ExactSpelling = true)]
static extern IntPtr VirtualQueryEx(
IntPtr hProcess,
IntPtr lpAddress,
out MEMORY_BASIC_INFORMATION lpBuffer,
IntPtr dwLength);
[StructLayout(LayoutKind.Sequential)]
public struct MEMORY_BASIC_INFORMATION
{
public IntPtr BaseAddress;
public IntPtr AllocationBase;
public int AllocationProtect;
public IntPtr RegionSize;
public int State;
public int Protect;
public int Type;
}
[Flags]
public enum AllocationType
{
Commit = 0x1000,
Reserve = 0x2000,
Decommit = 0x4000,
Release = 0x8000,
Reset = 0x80000,
Physical = 0x400000,
TopDown = 0x100000,
WriteWatch = 0x200000,
LargePages = 0x20000000
}
[Flags]
public enum MemoryProtection
{
Execute = 0x10,
ExecuteRead = 0x20,
ExecuteReadWrite = 0x40,
ExecuteWriteCopy = 0x80,
NoAccess = 0x01,
ReadOnly = 0x02,
ReadWrite = 0x04,
WriteCopy = 0x08,
GuardModifierflag = 0x100,
NoCacheModifierflag = 0x200,
WriteCombineModifierflag = 0x400
}
[Flags]
public enum FreeType
{
Decommit = 0x4000,
Release = 0x8000
}
}
}
Pourquoi voulez-vous faire cela? –
N'a-t-il pas une surcharge qui reçoit un IntPtr? Un IntPtr est une valeur 64 bits sur 64 plates-formes, plus grande que Int32.MaxInteger. –
Vitor, Mon code de test échoue: IntPtr myp = new IntPtr (longueur); // longueur = 3 000 000 000 Donc, il semble que ce n'est pas grand sur ma machine 64 bits ... – ManInMoon