Aide sur le file mapping

Résolu
Reality31600 Messages postés 10 Date d'inscription dimanche 2 mai 2010 Statut Membre Dernière intervention 30 mai 2011 - 14 mars 2011 à 11:44
Bacterius Messages postés 3792 Date d'inscription samedi 22 décembre 2007 Statut Membre Dernière intervention 3 juin 2016 - 16 mars 2011 à 20:29
Bonjour,

J'ai réalisé une application de Copie de fichier a l'aide de thread et de la methode des flux mais j'ai été un peu déçu de la vitesse, notamment lors de copie de gros fichiers,puis j'ai entendu parler du File Mapping qui apparemment pourrait certainement m'offrir une methode de copie beaucoup plus rapide puisque l'accès aux fichiers seraient largement plus rapide.

Je suis débutant donc je fais des essaies, mais je n'ai pas réussi a rendre opérationnel la copie d'un fichier (le fichier test2.iso est bien crée mais il fais 0ko au lieu de 3Go ^^ )

Voici mon code :

procedure TForm1.Button1Click(Sender: TObject);
var
FFilehandle: THANDLE;
FFileMap: THANDLE;
FmappingPtr: pchar;
hFile2:    THANDLE ;
SizeFile1,BytesWritten: DWORD ;
begin

FFilehandle := CreateFile('titan.iso',
GENERIC_WRITE OR GENERIC_READ,
FILE_SHARE_READ OR FILE_SHARE_WRITE,
  nil,
  OPEN_EXISTING,
  FILE_ATTRIBUTE_NORMAL,
  0);
  if (FFilehandle <> INVALID_HANDLE_VALUE) then
  begin
    FFileMap := CreateFileMapping(FFileHandle, // handle to file to map
      nil, // optional security attributes
      PAGE_READWRITE, // protection for mapping object
      0, // high-order 32 bits of object size
      2*1024, // low-order 32 bits of object size
      0); //
    if (FFileMap <> NULL) then
    begin
      FMappingPtr := MapViewOfFile(FFileMap,
        FILE_MAP_WRITE,
        0,
        0,
        0);
      if Assigned(FMappingPtr)   then
      begin
        // Manipulation de FMappingPtr

   hFile2 := CreateFile('test.iso',GENERIC_WRITE,0,nil,CREATE_ALWAYS,FILE_ATTRIBUTE_NORMAL,0);
   if (hFile2 <> INVALID_HANDLE_VALUE) then
   begin
    SizeFile1 := GetFileSize(FFilehandle,NIL); // recupere taille du fichier 1

   WriteFile(hFile2,Fmappingptr,SizeFile1,&BytesWritten,NIL); // Transfert la mémoire mappé dans file 2

   // libere les ressources
   end
   else   MessageBox(0,'Impossible de lire le fichier mappé','Error',0);

   UnmapViewOfFile(Fmappingptr);
   CloseHandle(FFileMap);
   CloseHandle(FFilehandle);
   CloseHandle(hFile2);

      end
      else
      begin
      	CloseHandle (FFileMap);
   	CloseHandle (FFileHandle);
   	MessageBox(0,'Impossible de lire le fichier mappé','Error',0);
      end;
    end
    else
    begin
      CloseHandle (FFilemap);
      MessageBox(0,'Impossible de mappe le fichier en mémoire','Error',0);
    end;
  end
else
  MessageBox(NULL,'Impossible d''ouvrir le fichier','Error',NULL);



   end;


Merci d'avance

17 réponses

Caribensila Messages postés 2527 Date d'inscription jeudi 15 janvier 2004 Statut Membre Dernière intervention 16 octobre 2019 18
16 mars 2011 à 14:35
Les différences entre les mesures de performances sont dûes au multitâche Windows.
On ne peut pas prévoir le temps processeur qui sera finalement alloué à ton application car le système gère la commutation des tâches qu'il doit effectuer en parallèle de façon non transparente. Et ça change tout le temps.
La seule possibilité laissée au programmeur est la faculté de jouer sur la priorité des Processes et des Threads. Mais il ne faut pas non plus en abuser et éviter d'utiliser la priorité maximum qui devrait être réservée au système (pour gérer des trucs comme le curseur et la souris ou la gestion de la mémoire, par exemple).

En général, pour mesurer les performances, on fait de nombreux tests et on ne retient que le temps minimum obtenu comme celui d'un test effectué dans l'environnement le plus favorable. Ce qui est faux, mais on s'en contente car le plus souvent il ne s'agit que de comparer différentes techniques et de ne retenir que la plus rapide.


_______________________________________
Soutenez Wikimédia France aujourd'hui : http://dons.wikimedia.fr
3
Bacterius Messages postés 3792 Date d'inscription samedi 22 décembre 2007 Statut Membre Dernière intervention 3 juin 2016 10
16 mars 2011 à 20:29
Oui car le file mapping requiert davantage d'initialisation que le block write, et que cette initialisation se fait bien sentir sur des petits fichiers.

Cordialement, Bacterius !
3
Bacterius Messages postés 3792 Date d'inscription samedi 22 décembre 2007 Statut Membre Dernière intervention 3 juin 2016 10
14 mars 2011 à 21:15
Non non l'accès aux fichiers n'est pas plus rapide, c'est juste plus simple d'utiliser le fichier. Les disques durs sont juste très lents - ils lisent/écrivent à un taux d'entre 50 et 100 Mo/s (comparé à 5Go/s pour la mémoire). Les plus récents SSD (très chers) vont jusqu'à 400 Mo/s. Y'a pas de moyen de contourner la lenteur du disque dur à part utiliser un autre médium de stockage plus rapide comme la mémoire (mais elle n'est pas infinie).

Cordialement, Bacterius !
0
Bacterius Messages postés 3792 Date d'inscription samedi 22 décembre 2007 Statut Membre Dernière intervention 3 juin 2016 10
14 mars 2011 à 21:16
Aussi les temps d'accès des DD mécaniques est assez long - 15 millisecondes en moyenne pour déplacer la tête d'écriture et commencer à lire. (alors qu'avec un SSD par exemple c'est instantané).

Cordialement, Bacterius !
0

Vous n’avez pas trouvé la réponse que vous recherchez ?

Posez votre question
Reality31600 Messages postés 10 Date d'inscription dimanche 2 mai 2010 Statut Membre Dernière intervention 30 mai 2011
14 mars 2011 à 21:22
Merci pour ton explication, du coup c'est quand même avantageux une methode de copie en utilisant le file mapping que TFileStream ?
Parce que j'ai déjà implémenté un logiciel de copie multi-thread qui utilise des TfileStream et je trouve sa lent et je cherche donc divers moyen d'être plus rapide
0
Bacterius Messages postés 3792 Date d'inscription samedi 22 décembre 2007 Statut Membre Dernière intervention 3 juin 2016 10
14 mars 2011 à 21:29
Avec le file mapping tu peux paralléliser des opérations de fichiers, c'est à dire que tu peux en faire plusieurs en même temps car le système d'occupe de tout, alors qu'avec un file stream ton application (en tout cas le thread depuis lequel l'opération est lancée) bloque jusqu'à ce que l'opération se termine.

Mais ne fais pas d'erreur : quand tu utilise du file mapping, ça n'écrit pas plus vite au disque dur - c'est juste le système s'en occupe en fond de tâche et ton application est libre pour faire d'autres trucs.

Donc le file mapping est une bonne méthode mais ne te leurre pas en pensant que ça écrira plus vite à la surface du disque - c'est faux.

Cordialement, Bacterius !
0
Reality31600 Messages postés 10 Date d'inscription dimanche 2 mai 2010 Statut Membre Dernière intervention 30 mai 2011
14 mars 2011 à 21:42
Non je ne dis pas que sa va écrire plus vite, je disais que "l'accès" au fichier sera plus performant :)
Merci tu m'aide a y voir un peu plus clair, mais comment je peux utiliser le file mapping exactement ? dans mon exemple mon code source ne marche pas et je sais vraiment pas d'ou sa viens :s
0
Caribensila Messages postés 2527 Date d'inscription jeudi 15 janvier 2004 Statut Membre Dernière intervention 16 octobre 2019 18
15 mars 2011 à 00:40
Salut,

@Reality31600
Outre le partage de données entre processes, le File Mapping pour l'accès à des fichiers disques peut être très intéressant quand on effectue des opérations diverses et variées dans le fichier. Comme te l'a expliqué Bacterius, la lecture et l'écriture de données est alors beaucoup plus rapide. Le goulet d'étranglement ne se passe que lorsqu'on lit ou re-écrit le fichier (ou partie) sur le DD.

Mais dans le cas que tu nous présentes (simple clonage de fichier, si j'ai bien compris), le File Mapping n'apportera rien car ça revient à utiliser la RAM comme un tampon. Tampon qu'il faut créer, dans lequel il faut écrire les données puis les lire et qu'il faut enfin libérer. Sans compter le risque d'obliger le système à swaper des pages sur le DD si la RAM est saturée, et dans ce cas c'est tout le système qui est ralenti.
Et ici, avec ton fichier de 3Go, tu dépasses allègrement les capacités du système et tu vas l'obliger de jongler avec la RAM et le DD. Il le fera sans doute de bonne grâce, mais il te présentera une facture libellée en temps.

J'utiliserais plutôt un truc de ce genre, avec CopyFrom :

var
  NewFile : TFileStream;
  OldFile : TFileStream;
begin
...
OldFile := TFileStream.Create(OldFileName, fmOpenRead or fmShareDenyWrite);
try
  NewFile := TFileStream.Create(NewFileName, fmCreate or fmShareDenyRead);
  try
    NewFile.CopyFrom(OldFile, OldFile.Size);
  finally  FreeAndNil(NewFile);  end;
finally  FreeAndNil(OldFile);  end;


Je pense que la RAM sert aussi de tampon dans ce cas, mais on peut supposer que c'est optimisé au mieux.
Pour de très gros fichiers, on peut peut-être aussi essayer de faire la copie en plusieurs fois en jouant sur la valeur du nombre d'octets copiés à chaque fois (OldFile.Size)... Mais cela mériterait de faire des tests.




_______________________________________
Soutenez Wikimédia France aujourd'hui : http://dons.wikimedia.fr
0
Bacterius Messages postés 3792 Date d'inscription samedi 22 décembre 2007 Statut Membre Dernière intervention 3 juin 2016 10
15 mars 2011 à 03:50
Comme te l'a expliqué Bacterius, la lecture et l'écriture de données est alors beaucoup plus rapide.

Non, justement, ça ne change absolument rien niveau performances du disque dur. En revanche, ça améliore les performances niveau application, car c'est le système qui s'occupe de gérer les accès au DD sans perturber ton appli, via la mémoire pagée. Mais le système en lui même n'écrira pas plus vite au DD que ton appli - c'est juste que le goulet d'etranglement est déplacé depuis l'application, dans le système. Victoire

Pour un fichier de 3Go, si tu fais une lecture séquentielle c'est facile, il suffit d'utiliser un file mapping en prenant CreateFileMapping avec des petits intervalles (genre 32 Mo) et de parcourir le fichier petit à petit. En revanche, si c'est à accès aléatoire, ça va faire mal, puisqu'il faut de toute façon plus ou moins 15 millisecondes au disque dur (mécanique en tout cas) pour déplacer la tête de lecture/écriture sur l'emplacement que tu cherches à lire, peu importe que tu lises 3Go ou 1 octet. Donc l'accès aléatoire, je ne vois qu'une solution, pager les données les plus lues. Mais à part ça il s'agit d'une limite physique de ton matériel (notre matériel) et on ne peut pas vraiment le contourner.

Cordialement, Bacterius !
0
Reality31600 Messages postés 10 Date d'inscription dimanche 2 mai 2010 Statut Membre Dernière intervention 30 mai 2011
15 mars 2011 à 10:16
@Caribensila c'est bien ce que j'ai utilisé :

FichierFlux := TFileStream.Create(TElementListe(Liste.Items[compteur]^)
      .sourcefichier, fmOpenRead);
  // Crée une instance de TFileStream, "fmOpenRead" permet d'ouvrir le fichier en lecture seule
  FFlux := TFileStream.Create(dest, fmOpenWrite or fmCreate); // "fmOpenWrite" ouvre le fichier en écriture seulement (remplace le contenu), "fmCreate" permet de créer le fichier si il n'existe pas

  try

    Readed := -1;

    lelabel.Caption := ObtenirNomfichier(PElementListe(Liste[compteur])
        .sourcefichier);

    laprogressbar.Min := 0;
    laprogressbar.Step := round(sizeof(Buffer)/1000);
    laprogressbar.Max := round(FichierFlux.Size/1000);

    while (FFlux.Position < FichierFlux.Size) and not(Readed = 0) Do
    begin
      // fichierflux.Size/sizeof(buffer)

      Readed := FichierFlux.Read(Buffer, sizeof(Buffer));
      Writed := FFlux.Write(Buffer, Readed);

      laprogressbar.stepit;

    end;
    // FFlux.CopyFrom(FichierFlux,FichierFlux.Size);
    // FFlux.CopyFrom(FichierFlux,FichierFlux.Size); //Copie le fux "FichierFlux" vers un autre flux "FFlux"
  finally
    laprogressbar.Position := FichierFlux.Size;
    FichierFlux.Free; // Libère le flux
    FFlux.Free;
  end;


Cela marche très bien mais j'essaye de trouver une autre façon, doit je continuer a essayer d'exploiter le file mapping pour espér un gain de temps en résumé?
0
Bacterius Messages postés 3792 Date d'inscription samedi 22 décembre 2007 Statut Membre Dernière intervention 3 juin 2016 10
15 mars 2011 à 10:25
doit je continuer a essayer d'exploiter le file mapping pour espér un gain de temps en résumé?

Ca dépend de ce que tu veux faire, il n'existe pas de solution générale à tous les problèmes d'accès de fichier. Ca dépend de la taille de tes fichiers, de la façon dont tu les lis/écris ... j'ai expliqué plus haut qu'avec le file mapping on ne peut obtenir qu'un gain illusoire pour l'application, et qui n'a de bénéfice que si l'application a d'autres choses à faire pendant la lecture/écriture du fichier. D'un autre côté, le file mapping est plus difficile à implémenter, en particulier pour les gros fichiers, et c'est difficile de changer la taille du fichier une fois qu'il est mappé. Je peux pas vraiment rajouter autre chose

Cordialement, Bacterius !
0
Reality31600 Messages postés 10 Date d'inscription dimanche 2 mai 2010 Statut Membre Dernière intervention 30 mai 2011
15 mars 2011 à 10:35
Merci de ton aide Bacterius je comprend maintenant comment marche le file mapping et effectivement mon application ne fais pas autre chose pendant la lecture/écriture du fichier car c'est une simple application de copie de fichier que j'ai essayer de rendre plus performant a l'exemple de teracopy, ou fastcopy qui gagne pas mal de temps par rapport a la copie windows.

Bien sur je travaille sous vista car sous seven la copie systeme a été grandement amélioré et il deviens très dur de la dépassé pour un novice comme moi :)

Bien sûr je connais des techniques qui pourrait me le permettre mais je pense pas avoir le niveau comme la copie asynchrone donc je cherche des moyens un peu plus facile meme si j'avoue que le file mapping je ne comprend pas grand chose a son implémentation même si je sais à quoi sa sert
0
Bacterius Messages postés 3792 Date d'inscription samedi 22 décembre 2007 Statut Membre Dernière intervention 3 juin 2016 10
15 mars 2011 à 10:44
Le file mapping en fait ça crée une image disque dans la mémoire vive, que tu peux modifier très rapidement (car la mémoire vive est très rapide). Le système, quand il a du temps, regarde les changements que tu as fait à cet espace mémoire et fait les modifs qui s'imposent au sein du disque dur lui-même - mais il le fait plus ou moins à l'aide d'un FileStream isolé dans un thread (les détails sont compliqués mais c'est comme ça en fait). Il y a des désavantages surtout liés à la taille du fichier, il est diffile voire impossible de la changer tant que le fichier est mappé (enfin faut le faire quoi). Mais pour ce que tu cherches à faire il n'y a pas de problème.

Je pense que les applications que tu mentionnes, pour aller plus vite, contournent carrément Windows et vont gratter directement sur le disque dur elles mêmes en appelant les fonctions de plus bas niveau (les plus proches du matériel), mais là c'est du lourd et je saurais même pas par où commencer.

Cordialement, Bacterius !
0
Caribensila Messages postés 2527 Date d'inscription jeudi 15 janvier 2004 Statut Membre Dernière intervention 16 octobre 2019 18
15 mars 2011 à 18:16
@Bacterius
Euh... Bein... Nous sommes d'accord. Je n'ai rien dit qui ne colle pas à tes explications

@Reality31600
Une opération de copie de fichier se passe toujours en deux temps: un Read et un Write. Et ces deux opérations utilisent un même Buffer.
Ce Buffer est toujours de la RAM physique. Que tu l'obtiennes par le File Mapping, par un Stream ou par un simple GetMem (comme le fait la fonction Delphi TStream.CopyFrom que je te proposais) ne changera pas grand'chose par rapport au temps d'accès au DD qui est infiniment plus long mais qui est aussi incontournable.

Malgré tout, on peut essayer d'optimiser ces opérations comme le font les applications dont tu parles:
- utiliser les APIs ReadFile et WriteFile
- optimiser la taille du Buffer qu'elles utilisent en la fixant à la taille de la Granularity de la RAM (64KB).
Il me semble même que $F000 Bytes soit une valeur optimisée pour ce buffer car les dernières adresses d'un bloc de granularity sont réservées par le système, je crois.
- l'utilisation adéquate des Threads et de leur priorité (très important).

C'est pour ces raisons que je disais que le File Mapping ne t'apportera pas grand'chose dans le cas d'un simple clonage de fichier. En revanche ce serait beaucoup plus rapide dans le cas où tu devrais faire des opération sur les données.

Quant à aller gratter directement sur le disque dur sans passer par un buffer, il faudrait que le DD soit équipé d'une tête de lecture et d'une tête d'écriture séparées. Je ne sais pas si ça existe.


_______________________________________
Soutenez Wikimédia France aujourd'hui : http://dons.wikimedia.fr
0
Bacterius Messages postés 3792 Date d'inscription samedi 22 décembre 2007 Statut Membre Dernière intervention 3 juin 2016 10
15 mars 2011 à 20:59
Il y a quelques attributs sympas dont tu peux te servir pour optimiser la copie d'un fichier, et qui a priori marchent aussi avec le file mapping. Dans CreateFile, tu peux spécifier FILE_FLAG_SEQUENTIAL_SCAN, qui améliore les performances dans le cadre d'une lecture/écriture séquentielle (ce qui est le cas ici). Tu peux aussi spécifier FILE_FLAG_NO_BUFFERING, qui dit au système de ne pas utiliser de cache intermédiaire entre ton application et le disque dur - mais il y a certaines conditions :
- le cache de ton application en mémoire doit avoir été initialisé avec VirtualAlloc (facile)
- tu dois lire/écriture le fichier par des blocs de taille multiple de la taille de secteur du disque dur - donc il faut lire des puissances de deux.

Il y a quelques autres flags je crois, tu peux les voir documentés sur la MSDN.

Cordialement, Bacterius !
0
Reality31600 Messages postés 10 Date d'inscription dimanche 2 mai 2010 Statut Membre Dernière intervention 30 mai 2011
16 mars 2011 à 10:47
J'ai enfin réussi mon FileMapping

Je suis assez déçu car mon 1er test n'est pas concluant :
Avec 4 Thread qui appel ma procédure de copie gràce a un filemapping je copie 950 fichiers (pour un total de 258Mo) en 58,11 Sec

En comparaison (toujours avec 4 thread qui appel ma procédure de copie):

CopyFileEx - 24,653 secondes
TFileStream - 27,503 secondes
BlockRead/BlockWrite - 7 secondes

Mais après c'est toujours pareil, mes copies ne sont jamais stable, parfois j'obtiendrais 79secondes avec un blockread/blockwrite , une autre fois 14 secondes, pareil pour toutes mes copies XD
Bon on va me dire c'est le cache qui fais sa , oué ben dans ce cas le cache il est pareil pour toutes les procédure de copie
0
Reality31600 Messages postés 10 Date d'inscription dimanche 2 mai 2010 Statut Membre Dernière intervention 30 mai 2011
16 mars 2011 à 17:23
D'accord, merci pour ton explication, cela va me permettre de peaufiner mon rapport de stage
Je viens d'effectuer des tests sur des differentes tailles de fichiers,
ils s'avèrent que le file mapping est "beaucoup" plus rapide lors de la copie de gros fichier ( de 700 Mo par exemple)
tandis qu'il deviens extrêmement lent dans la copie de nombreux fichiers de petites tailles.
0
Rejoignez-nous