2010-08-18 3 views
6

J'ai un ensemble très grand nombre de fichiers binaires où plusieurs milliers lues et traitées images brutes de vidéo sont séquentiellement, et je suis maintenant de l'optimiser comme il semble être plus liée CPU que I/O-lié..NET Read Binary File Performance

Les cadres sont actuellement en cours de lecture de cette manière, et je soupçonne que c'est le plus grand coupable:

private byte[] frameBuf; 
BinaryReader binRead = new BinaryReader(FS); 

// Initialize a new buffer of sizeof(frame) 
frameBuf = new byte[VARIABLE_BUFFER_SIZE]; 
//Read sizeof(frame) bytes from the file 
frameBuf = binRead.ReadBytes(VARIABLE_BUFFER_SIZE); 

Ne serait-il faire une grande différence dans .NET pour réorganiser les E/S pour éviter créer tous ces nouveaux tableaux d'octets avec chaque image?

Ma compréhension du mécanisme d'allocation de mémoire de .NET est faible que je viens d'un pur C/C++ fond. Mon idée est de réécrire ceci pour partager une classe de tampon statique qui contient un très grand tampon partagé avec un entier gardant la trace de la taille réelle du cadre, mais j'aime la simplicité et la lisibilité de l'implémentation actuelle et je préférerais la garder si CLR gère déjà cela d'une manière que je ne connais pas.

Toute entrée serait très apprécié.

+5

Avez-vous exécuté un profileur pour vous assurer que les performances ne proviennent pas d'autres sources? Ou êtes-vous juste allé et supposé "Alors c'est probablement ça"? –

+0

Salut David, J'ai couru le profileur de performance à plusieurs reprises et cette méthode particulière est la plus chère. Par conséquent, je cherche à voir si cette méthode "new byte []" est un tueur de performance évident dans .NET. En tant que programmeur C, cela ressemble à des milliers d'instructions "malloc" pour chaque tampon, ce qui serait certainement plus lent qu'un tampon réutilisé. – rnd

Répondre

7

Vous n'avez pas besoin de init frameBuf si vous utilisez binRead.ReadBytes - vous obtiendrez un nouveau tableau d'octets qui remplacera celui que vous venez de créer. Cela crée un nouveau tableau pour chaque lecture, cependant.

Si vous voulez éviter de créer un ensemble de tableaux d'octets, vous pouvez utiliser binRead.Read, ce qui met les octets dans un tableau que vous lui fournissez. Si d'autres threads utilisent le tableau, ils verront le contenu de celui-ci changer juste devant eux. Assurez-vous de pouvoir en finir avec le tampon avant de le réutiliser.

+0

Merci de m'avoir fait remarquer - je suis sûr que mon allocation redondante la ralentit sensiblement.Et le tableau partagé statique est exactement ce que je considère, mais si le gain de performance n'est pas grand par rapport à la création des tableaux d'octets, je préfère rester avec la solution élégante pour la même complication que vous avez décrite (accès partagé) . – rnd

1

Vous devez faire attention ici. Il est très facile d'obtenir des résultats de test complètement faux sur un tel code, des résultats qui ne se reproduisent jamais en utilisation réelle. Le problème est le cache du système de fichiers, il va mettre en cache les données que vous lisez à partir d'un fichier. Les problèmes commencent lorsque vous exécutez votre test encore et encore, peaufinant le code et recherchant des améliorations.

La seconde, et les temps suivants vous exécutez le test, les données ne sont plus se détache du disque. Il est toujours présent dans le cache, il suffit d'une copie mémoire-à-mémoire pour l'intégrer dans votre programme. C'est très rapide, une microseconde de frais généraux plus le temps nécessaire pour copier. Ce qui fonctionne à des vitesses de bus, au moins 5 gigaoctets par seconde sur les machines modernes.

Votre test révélera maintenant que vous passez beaucoup de temps sur l'affectation de la mémoire tampon et le traitement des données, par rapport de la quantité de temps consacré à la lecture des données.

Ceci sera rarement reproduit en utilisation réelle. Les données ne seront pas encore dans le cache, maintenant le lecteur de disque sluggy doit rechercher les données (plusieurs millisecondes) et il doit être lu sur le plateau de disque (quelques dizaines de mégaoctets par seconde, au mieux). La lecture des données prend maintenant trois bonnes années sur quatre. Si vous avez réussi à accélérer deux fois le traitement, votre programme ne fonctionnera que 0.05% plus rapidement. Donner ou prendre.

+0

C'est un bon point, mais je cours mes tests sur un jeu de données qui éclipse la mémoire de ma machine de plusieurs gigaoctets. Ce qui me préoccupe, c'est le fait qu'un code similaire dans mon ancienne bibliothèque C++ traite cet ensemble de données en moins de la moitié du temps que cela prend. Cependant, j'ai remarqué que le profil avertit que 2,826 pages par/s sont écrites sur le disque et que l'application peut être liée à la mémoire. Je ne dispose pas explicitement de ces tableaux - ceux-ci pourraient-ils être mis en cache avant que le GC ne les désaffecte? – rnd

+2

Ces tampons sont probablement volumineux, plus de 85 Ko. Ce qui les obtient alloués dans le LOH. Ils seront coincés là pendant un moment, il faut une collection gen # 2. Rien ne vient gratuitement, la réutilisation des tampons quand ils sont grands est aussi une bonne stratégie dans .NET. –

+0

Si vous souhaitez forcer le chargement du fichier à partir du disque, effacez le cache de Windows, comme indiqué dans cette question: http://stackoverflow.com/q/478340/80525 – BKewl