2009-07-05 10 views
5

J'ai un petit programme qui lit et écrit des fichiers sur le disque. En le décomposant au niveau le plus simple, il lit les octets d'un flux de fichier et les écrit dans un autre. Il s'acquitte bien de ses fonctions, mais ce n'est pas la chose la plus rapide.Quel est le moyen le plus rapide pour lire/écrire sur le disque dans .NET?

J'ai vu d'autres applications qui peuvent déchirer un gigaoctet ou plus de lectures/écritures à des vitesses incroyables. Évidemment, ils fonctionnent plus près du métal qu'une petite application .NET. Quelles sont les API .NET les plus efficaces pour le streaming vers/depuis le disque? Quelles API win32 sont disponibles (et valent p/invoquant) pour un accès rapide au disque?

+3

Je ne vois pas pourquoi les appels WinAPI devraient être plus rapides que les classes .NET - après tout, ces derniers utilisent l'ancien en interne. En dehors de cela, un fichier mappé en mémoire (http://en.wikipedia.org/wiki/Memory_mapped_file) serait peut-être approprié? – Noldorin

+0

Pourquoi Dot.net aurait plus d'une façon d'écrire dans un fichier? Lire et écrire des fichiers est assez simple et cela n'a aucun sens d'avoir une forme "rapide" et "lente" - car personne n'utiliserait la version "lente" étant donné que les deux ont les mêmes objectifs. –

+0

Dans une demi-heure, je pourrais mettre en place un test comparant les opérations de fichier .net (implémentations naïves, peut-être, qui fait partie de la question) et une application native avec IO intensive (comme QuickPAR) qui va fermer les portes .NET app. C'est le but de la question - Comment atteindre un débit de disque optimal dans .NET? – Will

Répondre

10

Les E/S de fichiers rapides concernent moins les appels d'API spécifiques que vous effectuez, mais plutôt la façon dont vous concevez votre application pour qu'elle fonctionne avec les E/S.

Si vous effectuez tous vos E/opérations O sur un seul thread de manière séquentielle, par exemple

  1. lecture de bloc dans la mémoire
  2. bloc de processus en mémoire d'une certaine manière
  3. Ecrire le bloc sur déposer
  4. Répétez jusqu'à ce que fait ...

vous désengorgement de la bande passante d'E/S du système dans les procès- Sing boucle d'un seul thread. Une conception alternative, mais plus compliquée consiste à multithread votre application pour maximiser le débit et éviter le temps d'attente. Cela permet au système de tirer parti à la fois de la bande passante du contrôleur de CPU et d'E/S simultanément. Une conception typique de ce ressemblerait à quelque chose comme:

  1. un (ou plusieurs) threads de travail lire les données à partir du disque et de les ajouter à une file d'attente d'entrée partagée
  2. un (ou plusieurs) threads de travail blocs de lecture de la commune file d'entrée, les traite et les ajoute à une file d'attente de sortie partagée
  3. Un (ou plusieurs) threads de travail lus traités bloqués à partir de la file d'attente de sortie partagée et les écrivent dans les fichiers de sortie appropriés.

Cette architecture n'est pas facile à concevoir correctement et nécessite un peu de réflexion pour éviter de créer des conflits de verrous en mémoire, ou submerger le système de demandes d'E/S simultanées. Vous devez également fournir des métadonnées de contrôle afin que l'état du traitement de sortie ne soit pas géré sur la pile d'appel d'un thread mais plutôt dans les files d'attente de travail d'entrée/sortie. Vous devez également vous assurer que vous transformez et écrivez la sortie dans le bon ordre, car avec les E/S multithread vous ne pouvez pas être sûr que le travail est placé dans la file d'attente d'entrée dans un ordre garanti. C'est compliqué - mais c'est possible, et cela peut avoir une énorme différence de débit par rapport à une approche en série.

Si vous avez vraiment le temps et que vous voulez réduire chaque once de performance du système, vous pouvez également utiliser I/O completion ports - une API relativement de bas niveau - pour optimiser le débit.

Bonne chance.

1

Avez-vous profilé votre application pour déterminer si l'E/S du disque était le goulot d'étranglement?

Quel type de matériel utilisez-vous? Quelle est la configuration matérielle?

Dans .NET, vous pouvez essayer l'espace de noms System.IO.File.

Pour les fonctions Win32, vous pouvez essayer les séries CreateFile, WriteFile, ReadFile.

Un exemple:

http://msdn.microsoft.com/en-us/library/bb540534(VS.85).aspx

C'est certainement pas coupé et séché. Tout est question d'essai et de mesure.

+0

Personnellement, je serais * très * surpris si le disque IO était le problème ... Je n'ai jamais eu de problèmes pour maxer les E/S du disque avec l'une des primitives .NET ... (sauf peut-être qu'il exécute .NET 1 où Je crois que les flux de fichiers n'ont pas de tampon intégré) – jerryjvl

+1

La question n'était pas de savoir comment, mais comment rapide. Merci pour l'astuce sur System.IO.File (sarcasme, ftw). – Will

0

BinaryReader et BinaryWriter avec une taille de tampon appropriée sont assez rapides. Si vous lisez dans des structures, l'approche dangereuse décrite in this article vous aidera à lire rapidement, et l'écriture est similaire. Je suis également d'accord avec la suggestion de vérifier à nouveau que les E/S sont vraiment le goulot d'étranglement. J'ai d'abord rencontré cet article en raison d'une telle erreur.

6

La prise en charge des fichiers .NET est suffisamment rapide (comparable aux fonctions natives Win32). Plusieurs options qui peuvent vous aider à améliorer vos performances:

  1. Si votre lecture/écriture est séquentielle, aider le gestionnaire de mise en cache en appliquant une stratégie appropriée - fournir RandomAccess or SequentalScan, lors de l'instanciation FileStream
  2. Pensez à utiliser une mémoire tampon plus grande pour le stockage Lire les données
  3. Si vous copiez de nombreux petits fichiers, vous pouvez d'abord lire plusieurs fichiers dans une mémoire tampon (voir 2), puis écrire les fichiers sur le disque
  4. Si les flux source et destination sont situés à différents endroits (c'est-à-dire, pas sur le même disque dur, peut-être un fichier sur le réseau, l'autre sur un disque dur local, etc.), vous pouvez utiliser le modèle asynchrone pour accélérer, lire les données en utilisant BeginRead, puis écrire des données en utilisant BeginWrite, et pendant que les données sont écrites, lisez le bloc de données suivant en utilisant BeginRead.
  5. Si vous pensez toujours que les performances ne suffisent pas (toutefois, de mon test, il est égal ou plus rapide que la copie interne de Windows), vous pouvez utiliser la fonction CopyFileEx Win32 (mais cette fonction fonctionne avec des fichiers).
+1

Une partie de la question est de l'utiliser correctement, que cette réponse essaie au moins d'accomplir. Merci. – Will

Questions connexes