2010-09-06 7 views
1

Je souhaite allouer plus de MaxInteger octets de mémoire.Comment allouer plus de MaxInteger octets de mémoire en C#

Marshall.AllocHGlobal() attend un nombre entier - donc je ne peux pas l'utiliser. Y a-t-il un autre moyen?

Mise à jour

J'ai changé la plate-forme x64, puis j'ai couru le code ci-dessous.

myp semble avoir la bonne longueur: environ 3.0G. Mais obstinément "tampon" maxes à 2.1G.

Une idée pourquoi?

var fileStream = new FileStream(
      "C:\\big.BC2", 
      FileMode.Open, 
      FileAccess.Read, 
      FileShare.Read, 
      16 * 1024, 
      FileOptions.SequentialScan); 
    Int64 length = fileStream.Length; 
    Console.WriteLine(length); 
    Console.WriteLine(Int64.MaxValue); 
    IntPtr myp = new IntPtr(length); 
    //IntPtr buffer = Marshal.AllocHGlobal(myp); 
    IntPtr buffer = VirtualAllocEx(
     Process.GetCurrentProcess().Handle, 
     IntPtr.Zero, 
     new IntPtr(length), 
     AllocationType.Commit | AllocationType.Reserve, 
     MemoryProtection.ReadWrite); 
    unsafe 
    { 
     byte* pBytes = (byte*)myp.ToPointer(); 
     var memoryStream = new UnmanagedMemoryStream(pBytes, (long)length, (long)length, FileAccess.ReadWrite); 
     fileStream.CopyTo(memoryStream); 
+12

Pourquoi voulez-vous faire cela? –

+0

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. –

+0

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

Répondre

1

Ceci n'est pas possible à partir du code managé sans un appel PinVoke et pour une bonne raison. Allouer autant de mémoire est généralement le signe d'une mauvaise solution à revoir.

Pouvez-vous nous dire pourquoi vous pensez avoir besoin d'autant de mémoire?

+0

Il essaie de lire un fichier qui ne rentre pas dans la mémoire. –

+2

Vous ne devriez toujours pas le faire de cette façon, le fichier devrait être lu en partie. –

+1

C'est une longue histoire !!! Si vous avez l'inclination - s'il vous plaît jeter un oeil à mes autres questions - ils sont tous sur le même sujet. Vous trouverez je détailler la raison là. Un grand merci – ManInMoon

7

Ce n'est pas possible sur le matériel courant dominant. Les mémoires tampons sont limitées à 2 gigaoctets, même sur les machines 64 bits. L'adressage indexé du tampon est toujours effectué avec un décalage signé 32 bits. Il est techniquement possible de générer du code machine qui peut indexer davantage, en utilisant un registre pour stocker le décalage, mais cela coûte cher et ralentit l'indexation des tableaux, même pour ceux qui ne dépassent pas 2 Go.

En outre, vous ne pouvez pas obtenir un tampon supérieur à environ 650 Mo hors de l'espace d'adressage disponible pour un processus 32 bits. Il n'y a pas assez de pages de mémoire contiguë disponibles car la mémoire virtuelle contient à la fois du code et des données à diverses adresses.

Des entreprises comme IBM et Sun vendent du matériel capable de faire cela.

+0

Êtes-vous sûr de cela? Je n'ai pas de machine avec plus de 2 Go à proximité pour le moment, donc je ne peux pas le tester, mais tout ce que je peux trouver indique que VirtualAlloc() peut allouer plus de 2 Go. En ce qui concerne l'adressage du tampon, il s'agit d'un problème distinct, mais si cela peut être fait pour les fichiers mappés en mémoire, cela peut certainement être fait aussi pour la mémoire normale. –

+1

Intéressant ... Il suffit de lire les manuels Intel, et en effet le déplacement et l'opérande immédiat ont tous les deux un maximum de 32 bits, même sur x64. – wj32

+0

En tout cas autour d'elle? – ManInMoon

0

Utilisez Marshal.AllocHGlobal(IntPtr). Cette surcharge traite la valeur du IntPtr en tant que quantité de mémoire à allouer et IntPtr peut contenir une valeur de 64 bits.

+0

Merci Rasmus - voir le commentaire de Hans ci-dessous - dommage! – ManInMoon

3

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

  1. Compile avec l'option de compilation /unsafe
  2. 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 
    } 
    } 
} 
+0

Merci Chris, mais je le fais effectivement savoir mais en lisant dans un bytearray 2D - donc tout est toujours dans mem. Et traiter chaque bytearray séparément. Mais c'est très lourd et j'ai dû faire quelques tours de cirque pour que ça marche. Étant donné que j'ai 32G dans ce serveur - je suis un peu vexé je ne peux pas l'utiliser directement ... – ManInMoon

+0

Salut Chris, merci pour le code. J'ai dû "répondre" pour vous montrer mon code de test. Pourriez-vous lire cela s'il vous plaît? – ManInMoon

0

D'un commentaire:

Comment puis-je créer une deuxième BinaryReader qui peut lire le même MemoryStream indépendamment?

var fileStream = new FileStream("C:\\big.BC2", 
     FileMode.Open, 
     FileAccess.Read, 
     FileShare.Read, 
     16 * 1024, 
     FileOptions.SequentialScan); 
    Int64 length = fileStream.Length; 
    IntPtr buffer = Marshal.AllocHGlobal(length); 
    unsafe 
    { 
     byte* pBytes = (byte*)myp.ToPointer(); 
     var memoryStream = new UnmanagedMemoryStream(pBytes, (long)length, (long)length, FileAccess.ReadWrite); 
     var binaryReader = new BinaryReader(memoryStream); 
     fileStream.CopyTo(memoryStream); 
     memoryStream.Seek(0, SeekOrigin.Begin); 
     // Create a second UnmanagedMemoryStream on the _same_ memory buffer 
     var memoryStream2 = new UnmanagedMemoryStream(pBytes, (long)length, (long)length, FileAccess.Read); 
     var binaryReader2 = new BinaryReader(memoryStream); 
    } 
0

Si vous ne pouvez pas le faire fonctionner comme vous le souhaitez directement, créer une classe pour fournir le type de comportement que vous voulez. Donc, pour utiliser de grands tableaux:

using System; 
using System.Collections.Generic; 
using System.Text; 
using System.IO; 

namespace BigBuffer 
{ 
    class Storage 
    { 
    public Storage (string filename) 
    { 
     m_buffers = new SortedDictionary<int, byte []>(); 
     m_file = new FileStream (filename, FileMode.Open, FileAccess.Read, FileShare.Read); 
    } 

    public byte [] GetBuffer (long address) 
    { 
     int 
     key = GetPageIndex (address); 

     byte [] 
     buffer; 

     if (!m_buffers.TryGetValue (key, out buffer)) 
     { 
     System.Diagnostics.Trace.WriteLine ("Allocating a new array at " + key); 
     buffer = new byte [1 << 24]; 
     m_buffers [key] = buffer; 

     m_file.Seek (address, SeekOrigin.Begin); 
     m_file.Read (buffer, 0, buffer.Length); 
     } 

     return buffer; 
    } 

    public void FillBuffer (byte [] destination_buffer, int offset, int count, long position) 
    { 
     do 
     { 
     byte [] 
      source_buffer = GetBuffer (position); 

     int 
      start = GetPageOffset (position), 
      length = Math.Min (count, (1 << 24) - start); 

     Array.Copy (source_buffer, start, destination_buffer, offset, length); 

     position += length; 
     offset += length; 
     count -= length; 
     } while (count > 0); 
    } 

    public int GetPageIndex (long address) 
    { 
     return (int) (address >> 24); 
    } 

    public int GetPageOffset (long address) 
    { 
     return (int) (address & ((1 << 24) - 1)); 
    } 

    public long Length 
    { 
     get { return m_file.Length; } 
    } 

    public int PageSize 
    { 
     get { return 1 << 24; } 
    } 

    FileStream 
     m_file; 

    SortedDictionary<int, byte []> 
     m_buffers; 
    } 

    class BigStream : Stream 
    { 
    public BigStream (Storage source) 
    { 
     m_source = source; 
     m_position = 0; 
    } 

    public override bool CanRead 
    { 
     get { return true; } 
    } 

    public override bool CanSeek 
    { 
     get { return true; } 
    } 

    public override bool CanTimeout 
    { 
     get { return false; } 
    } 

    public override bool CanWrite 
    { 
     get { return false; } 
    } 

    public override long Length 
    { 
     get { return m_source.Length; } 
    } 

    public override long Position 
    { 
     get { return m_position; } 
     set { m_position = value; } 
    } 

    public override void Flush() 
    { 
    } 

    public override long Seek (long offset, SeekOrigin origin) 
    { 
     switch (origin) 
     { 
     case SeekOrigin.Begin: 
     m_position = offset; 
     break; 

     case SeekOrigin.Current: 
     m_position += offset; 
     break; 

     case SeekOrigin.End: 
     m_position = Length + offset; 
     break; 
     } 

     return m_position; 
    } 

    public override void SetLength (long value) 
    { 
    } 

    public override int Read (byte [] buffer, int offset, int count) 
    { 
     int 
     bytes_read = (int) (m_position + count > Length ? Length - m_position : count); 

     m_source.FillBuffer (buffer, offset, bytes_read, m_position); 

     m_position += bytes_read; 
     return bytes_read; 
    } 

    public override void Write(byte[] buffer, int offset, int count) 
    { 
    } 

    Storage 
     m_source; 

    long 
     m_position; 
    } 

    class IntBigArray 
    { 
    public IntBigArray (Storage storage) 
    { 
     m_storage = storage; 
     m_current_page = -1; 
    } 

    public int this [long index] 
    { 
     get 
     { 
     int 
      value = 0; 

     index <<= 2; 

     for (int offset = 0 ; offset < 32 ; offset += 8, ++index) 
     { 
      int 
      page = m_storage.GetPageIndex (index); 

      if (page != m_current_page) 
      { 
      m_current_page = page; 
      m_array = m_storage.GetBuffer (m_current_page); 
      } 

      value |= (int) m_array [m_storage.GetPageOffset (index)] << offset; 
     } 

     return value; 
     } 
    } 

    Storage 
     m_storage; 

    int 
     m_current_page; 

    byte [] 
     m_array; 
    } 

    class Program 
    { 
    static void Main (string [] args) 
    { 
     Storage 
     storage = new Storage (@"<some file>"); 

     BigStream 
     stream = new BigStream (storage); 

     StreamReader 
     reader = new StreamReader (stream); 

     string 
     line = reader.ReadLine(); 

     IntBigArray 
     array = new IntBigArray (storage); 

     int 
     value = array [0]; 

     BinaryReader 
     binary = new BinaryReader (stream); 

     binary.BaseStream.Seek (0, SeekOrigin.Begin); 

     int 
     another_value = binary.ReadInt32(); 
    } 
    } 
} 

je partage le problème en trois classes:

  • Stockage - où les données réelles sont stockées, utilise un système paginée
  • BigStream - une classe de cours d'eau qui utilise la classe de stockage pour sa source de données
  • IntBigArray - une enveloppe autour du type de stockage qui fournit une interface de réseau de int

Ce qui précède peut être considérablement amélioré, mais il devrait vous donner des idées sur la façon de résoudre vos problèmes.

+0

Merci Skizz - je vous remercie de partager cette – ManInMoon

Questions connexes