2017-01-12 4 views
1

J'ai de gros fichiers binaires que je lis dans des structures, puis j'ajoute ces structures à une liste pour pouvoir les parcourir plus tard. Tout fonctionne bien, sauf que la lecture dans les structures se fait plus lentement que prévu. Voici le code correspondant:Comment augmenter les performances de lecture de gros fichier binaire dans les structures?

//128 bytes total 
[StructLayout(LayoutKind.Sequential, Pack = 1, CharSet = CharSet.Ansi)] 
public struct RAMPrec2 
{ 
    public double now;     // Days in double precision (see note 1) 
    public int VehLatitude;   // Milliarcseconds (see note 2) 
    public int VehLongitude;   // Milliarcseconds (see note 2) 
    public short VehLatUncertainty; // Meters as 16-bit int 
    public short VehLonUncertainty; // Meters as 16-bit int 
    public short Reserved1;  // Meters as 16-bit int (see note 3) 
    public short Reserved2;  // Meters as 16-bit int 
    public float VehAltitude;   // Meters as a float 
    public float VehFirstRet;   // Meters as a float 
    public float VehDepth;    // Meters as a float 
    public float VehSpeed;    // Knots as a float 
    public float VehHeading;   // degrees as a float (e.g. 0.0 - 359.9999) 
    public float VehCourse;   // degrees as a float (e.g. 0.0 - 359.9999) 
    public float VehRoll;    // degrees as a float (positive is counterclockwise roll) 
    public float VehPitch;    // degrees as a float (negative is downward pitch) 
    public float VehVOS;    // Meters/sec 
    public int DisplayNorthing;  // Centimeters as 32-bit integer 
    public int DisplayingEasting;  // Centimeters as 32-bit integer 
    public int OriginalLatitude;  // Milliarcseconds as 32-bit integer 
    public int OriginalLongitude;  // Milliarcseconds as 32-bit integer 
    public int DeltaNorthing;   // Centimeters as 32-bit integer 
    public int DeltaEasting;   // Centimeters as 32-bit integer 
    public short FixFlags;    // 16 bit flags. (note 5) 
    [MarshalAs(UnmanagedType.ByValArray, SizeConst = 8)] // 8 sets of 32-bit values,sensor specific (note 3) 
    public int[] SData; 
    public ushort DataAvailFlags;  // 16 bit mask field (note 4) 
    public ushort QA_Flag;    // 16 bit mask field (note 6) 
    public short EventFlag;   // 2-byte reserved field 
    public float Reserved3;   // 4-byte reserved field 
} 

public Boolean ReadRampFileType2(List<string> rampPaths) //rampPaths is just a list of filepaths for each binary file to be read 
{ 
    for (int i = 0; i < rampPaths.Count; i++) 
    { 
     try 
     { 
      using (var stream = new FileStream(rampPaths[i], FileMode.Open, FileAccess.Read, FileShare.None)) //open up a stream on the specified file 
      { 
       stream.Position = 8192; //skip header 

       while (stream.Position < (stream.Length)) //while not at end of file 
       { 
        RAMPrec2 ramp = ReadRecFromStream<RAMPrec2>(stream, Marshal.SizeOf(typeof(RAMPrec2))); //read in each record to the ramp struct 
        AddRecDataToListsType2(ramp, vehicles[i]); 
       } 
      } 
     } 
     catch (Exception e) //something went wrong 
     { 
      return false; 
     } 
    } 
    return true; 
} 

private T ReadRecFromStream<T>(Stream stream, int size) where T : struct 
{ 
    byte[] buffer = new byte[Marshal.SizeOf(typeof(T))]; 
    stream.Read(buffer, 0, size); 
    GCHandle handle = GCHandle.Alloc(buffer, GCHandleType.Pinned); 
    try 
    { 
     return (T)Marshal.PtrToStructure(handle.AddrOfPinnedObject(), typeof(T)); 
    } 
    finally 
    { 
     handle.Free(); 
    } 
} 

//I have a vehicle class with a bunch of lists. This adds the data from the structs I read in to these lists 
public void AddRecDataToListsType2(RAMPrec2 ramp, VehicleModel vehicle) 
{ 
    vehicle.NowTime.Add(ramp.now); 
    vehicle.VehLat.Add(ramp.VehLatitude/MILLIARCTODEG); 
    vehicle.VehLong.Add(ramp.VehLongitude/MILLIARCTODEG); 
    vehicle.VehHead.Add(ramp.VehHeading); 
    vehicle.VehSpeed.Add(ramp.VehSpeed); 
    vehicle.VehTWD.Add(ramp.VehAltitude + ramp.VehDepth); 
    vehicle.VehAlt.Add(ramp.VehAltitude); 
    vehicle.VehDep.Add(ramp.VehDepth); 
    vehicle.VehRoll.Add(ramp.VehRoll); 
    vehicle.VehPit.Add(ramp.VehPitch); 
} 

boucle I dans la liste des fichiers binaires que j'ai et ouvrir un flux de fichiers sur chacun, passez à la position où la première « struct » apparaît dans le fichier binaire, puis lire que dans une structure, qui a ensuite ses champs ajoutés à un tas de listes, puis je répète jusqu'à la fin du fichier. C'est lent, et je pense qu'il doit y avoir une meilleure façon de faire les choses.

+0

Combien et quelle taille sont vos fichiers? Eh bien, il semble que vous tombez sur les limites de vitesse de lecture de stockage. Il n'y a rien que vous pouvez faire en dehors de la mise à niveau vers SSD. –

+0

Seulement comme 400mb, peut-être plus grand, avec 1 'struct' pour chaque 128 octets. Mais il y a des multiples de ces fichiers. 4 tout au plus en ce moment. – pfinferno

+1

utilise 'while (! Stream.EndOfStream)'. Et chaque fois que vous appelez '.Length', le système doit vérifier la longueur du flux ou du tableau. Il y a beaucoup de raisons pour lesquelles c'est lent. Je verrai si je peux vous construire une méthode plus rapide (la première chose est de se débarrasser de toutes les allocations et appels de méthode si vous voulez la meilleure performance.) –

Répondre

1

Ceci est un candidat idéal pour la cartographie de la mémoire. Au lieu de lire les structures en mémoire, vous pouvez créer une vue mappée sur votre fichier de données et accéder à vos structures comme si elles étaient en mémoire. Bien que je n'ai pas utilisé le mappage de la mémoire en C#, en mode non sécurisé, vous pouvez éventuellement utiliser un RAMPrec2* directement sur votre vue mappée et utiliser ce pointeur pour faire défiler vos enregistrements/structures, comme en C++. Voir here pour un exemple.

Vous pouvez même éliminer la création des listes séparées, car tous les champs peuvent être accédés directement à travers la vue mappée.

+0

J'aimerais utiliser la cartographie de la mémoire, j'ai lu que c'est probablement le moyen de allez après avoir fait des recherches après avoir vu votre réponse. C'est un peu confus pour moi cependant. – pfinferno

+0

@pfinferno Vous pourriez le comparer à la lecture de votre fichier dans un grand tampon (ou plusieurs plus petits - «vues mappées») et accéder à vos structures directement dans ce tampon. Sauf avec le mappage de la mémoire, vous n'avez pas besoin d'allouer un tampon ou de lire les données - vous travaillez directement sur le cache du disque du système d'exploitation. –

1

Try this ...

// this would be how to call... 

foreach (var item in ReadFile<RAMPrec2>("testFile.bin", 0).Select((v, i) => new { Value = v, Index = i })) 
{ 
    var vehicle = vehicles[item.Index]; 
    var ramp = item.Value; 
    vehicle.NowTime.Add(ramp.now); 
    vehicle.VehLat.Add(ramp.VehLatitude/MILLIARCTODEG); 
    vehicle.VehLong.Add(ramp.VehLongitude/MILLIARCTODEG); 
    vehicle.VehHead.Add(ramp.VehHeading); 
    vehicle.VehSpeed.Add(ramp.VehSpeed); 
    vehicle.VehTWD.Add(ramp.VehAltitude + ramp.VehDepth); 
    vehicle.VehAlt.Add(ramp.VehAltitude); 
    vehicle.VehDep.Add(ramp.VehDepth); 
    vehicle.VehRoll.Add(ramp.VehRoll); 
    vehicle.VehPit.Add(ramp.VehPitch); 
} 

C'est là que la magie se produit.

static IEnumerable<T> ReadFile<T>(string fileName, int offset) where T : struct 
{ 
    using (var reader = File.OpenRead(fileName)) 
    { 
     var sizeOf = Marshal.SizeOf(typeof(T)); 
     var ptr = Marshal.AllocHGlobal(sizeOf); 
     try 
     { 
      reader.Position = offset; 
      var fileLength = reader.Length + reader.Position; 
      var buffer = new byte[sizeOf]; 
      for (var p = reader.Position; p < fileLength; p += sizeOf) 
      { 
       reader.Read(buffer, 0, sizeOf); 
       Marshal.Copy(buffer, 0, ptr, sizeOf); 
       var ret = Marshal.PtrToStructure(ptr, typeof(T)); 
       var str = (T)ret; 
       yield return str; 
      } 
     } 
     finally 
     { 
      Marshal.FreeHGlobal(ptr); 
     } 
    } 
}