Publicité
+ Répondre à la discussion
Affichage des résultats 1 à 4 sur 4
  1. #1
    Membre actif Avatar de Argol_Medusa
    Homme Profil pro Yann
    Ingénieur Radiofréquences
    Inscrit en
    août 2005
    Messages
    195
    Détails du profil
    Informations personnelles :
    Nom : Homme Yann
    Localisation : France

    Informations professionnelles :
    Activité : Ingénieur Radiofréquences
    Secteur : High Tech - Électronique et micro-électronique

    Informations forums :
    Inscription : août 2005
    Messages : 195
    Points : 185
    Points
    185

    Par défaut TMemoryStream : incompréhension

    Bonjour à tous,

    je suis en train de me poser de sérieuses questions sur les TMemoryStream.

    Je souhaite mettre une grosse quantité de données en mémoire, afin de les traiter de manière rapide en __int64 , en utilisant la pleine puissante de mon processeur 64 bits.

    Pour se faire j'utilise la méthode LoadFromFile pour charger des fichiers, et celle-ci :

    Code :
    __int64 __fastcall CopyFrom(TStream* Source, __int64 Count);
    Ok, pas de souci, sauf que :

    void __fastcall WriteBuffer(const void *Buffer, int Count);
    ne permet d'écrire que 32K de données ??? ( int et pas __int64 )

    J'essaye tout de même, la limitation de 32K ne semble pas être effective, car j'arrive à traiter de grosses quantitées de données qui dépassent largement les 32K, première surprise, je ne trouve pas l'overload de la fonction en __int64

    Seconde surprise, je charge progressivement des fichiers, jusqu'à 230Mo :

    Code :
    1
    2
    3
    4
    5
    6
    7
    8
     
                    // copier ce fichier à la suite dans le bigbuffer
    		TMemoryStream *a;
    		a = new TMemoryStream();
    		a->LoadFromFile(Nom_Fichier);
    		char *buffer;
    		buffer = (char*) a->Memory;
    		this->BigBuffer->WriteBuffer(buffer,a->Size);
    et là BIM :

    Le projet ifs.exe a déclanché la classe d'exception EOutOfMemory avec le message "mémoire insuffisante'


    What the ... ???

    Je teste vite fait pour vérifier que ce n'est pas un problème de limitation windows ou quelque chose dans le genre :


    Code :
    1
    2
    3
    4
    5
    6
    7
     
    unsigned __int64 taille = 1024*1024*550;
    MegaBuffer = new char[taille];
    for (unsigned __int64 i = 0; i < taille; i++)
    {
    	MegaBuffer[i]=0x66;
    }
    Ce dernier code fonctionne très bien, en CTRL ALT SUPR je vois bien la mémoire de l'exécutable qui monte à plus de 550Mo


    Un habitué des TMemoryStream pourrait-il m'éclairer? je dois rater un truc là
    (La doc embarquée sur le C++ Builder ne me donne aucune indication intéressante ).
    Désolé, on savait pas que c'était impossible, alors on l'a fait

  2. #2
    Expert Confirmé Sénior Avatar de ShaiLeTroll
    Homme Profil pro
    Développeur C++\Delphi
    Inscrit en
    juillet 2006
    Messages
    10 050
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 33
    Localisation : France

    Informations professionnelles :
    Activité : Développeur C++\Delphi
    Secteur : High Tech - Électronique et micro-électronique

    Informations forums :
    Inscription : juillet 2006
    Messages : 10 050
    Points : 14 266
    Points
    14 266

    Par défaut

    Citation Envoyé par Argol_Medusa Voir le message
    Je teste vite fait pour vérifier que ce n'est pas un problème de limitation windows ou quelque chose dans le genre :
    ton test est-il dans la même appli, même contexte, une fuite mémoire dans le projet réel ?

    Citation Envoyé par Argol_Medusa Voir le message
    Je souhaite mettre une grosse quantité de données en mémoire, afin de les traiter de manière rapide en __int64 , en utilisant la pleine puissante de mon processeur 64 bits.
    Quand tu évoques une grosse quantité sur un processeur 64 bits,
    550Mo cela reste possible en 32bits, étrange d'avoir une erreur, je pense à une fuite mémoire !
    Pense au SWAP aussi, si tu n'as pas la RAM physique, il va finalement écrire dans le fichier d'échange !
    Ton traitement sera au final plus lent !

    En plus, tu parle de 64Bits, mais c'est l'adresse d'un pointeur ou la taille d'un fichier, ce n'est qu'une valeur, cela n'améliore pas les performances d'être en 32 ou 64 bits, cela permet d'adresse plus de mémoire certe mais cela ne change pas les calculs surtout pour un TStream

    J'ai l'impression que tu confonds la taille des registres dont découlent les possibilités d'adressage avec la puissance de calcul ?!

    Dans le cas du TFileStream, en Win32 ou Win64,
    ReadBuffer\Read utilise toujours la fonction RTL FileRead qui encapsulent l'API ReadFile qui est toujours une version 32 bit,
    Seek (même la 64Bits) utile la fonction RTL FileSeek qui encaspulent l'API SetFilePointer qui utilise deux DWORD séparé pour reconstituer un __int64 donc au final, tu n'utilises même pas un registre 64 mais deux registre 32

    Dans le cas du TMemoryStream, allocation maximale à 2Gio

    Tu semble connaitre déjà la limite 2Go pour un process, j'ignore les limitation en 64, avec Delphi historiquement le compilateur refuse des variables de plus de 2Go (par exemple un tableau de 2^29 integer)
    Win32 lui-même refuse aussi plus de 2Go (que l'on peut passer à 3Go) par processus
    Qu'en est-il de Win64 et Delphi 64 ?
    C++ Builder est-il en 64 Bits, je ne crois pas, j'ignore les limites du langage à ce sujet mais le MemoryManager Delphi utilisé par TStream et ses descendants doivent limiter aussi la taille de variable à 2Go

    Un sujet à lire Problème de mémoire avec Delphi-5 sous 64 bits
    Autre élément qui pourrait t'intéresser :
    GetHeapStatus
    Delphi 2006+ : GetMemoryManagerState

    Windows : GetProcessMemoryInfo ou GlobalMemoryStatus

    Sujet connexe : RAM non utilisée

    voir Prise en charge de mémoire volumineuse disponible dans Windows Server 2003 et Windows 2000, article expliquant le commutateur /PAE et le commutateur /3GB
    Il doit y en avoir d'autres pour Vista\Seven, en plus WOW64 doit jouer lorsqu'un processus 32bits est utilisé sur un 64bits


    Citation Envoyé par Argol_Medusa Voir le message
    Pour se faire j'utilise la méthode LoadFromFile pour charger des fichiers, et celle-ci :
    Code :
    __int64 __fastcall CopyFrom(TStream* Source, __int64 Count);
    Pour LoadFromStream, tu peux effectivement copier un TFileStream vers un TMemoryStream, cela utilise CopyFrom en interne
    Pour LoadFromFile, c'est ni plus ni moins qu'un wrapper de TFileStream\LoadFromStream
    CopyFrom utilise un tampon de 60Kio pour la méthode abstraite Read() et boucle jusqu'à ce que la quantitée de donnée restant à copier atteignent zéro
    Si tu veux copier plusieurs Gio cela fera via quelques milliers d'itérations de 60Kio

    Citation Envoyé par Argol_Medusa Voir le message
    WriteBuffer ne permet d'écrire que 32K de données ??? ( int et pas __int64 )
    32K ??? D'où te viens c'est limite ???
    32bit donne une limite de 2^31 = 2Gio !
    Tu devrais lire le code du ReadBuffer ou WriteBuffer, il utilise Read ou Write, en général, cela lit le buffer d'un seul coup !
    Mais ils ont prévu pour des Stream que nous pourrions développer qui forcerait une limitation de buffer, d'où la boucle sur Stream en utilisant Read ou Write jusqu'à ce que la quantité de données souhaitée soit lues ou écrites

    Ensuite Read ou Write n'existe qu'en version 32Bit car Delphi Win32 est historiquement limité à 2Go de données pour un buffer (le paramètre de ces fonctions ne peut pas dépasser 2Go donc un Longint suffit), c'est surement lié au Win32 qui en gère que 2Go pour un processus, à l'époque, en 1998 (Delphi 3), on une grosse machine c'était 256Mo ! Il était inconsevable de charger plus de 64Mo dans un programme, le SWAP étant tellement utilisé que les performances étaient plus mauvaises qu'avec une lecteur séquentiel de fichier !

    Idem pour ReadBuffer ou WriteBuffer, il y a la même limite, j'ignore si le compilateur 64Bits permet de dépasser une variable de 2Go mais C++Builder est encore 32 bits !? XE3 n'apporte pas encore le 64 Bits en C++ ? Je croyais que c'est pour la prochaine version ?!

    Pour CopyFrom, cela supporte le 64bits parce que le receveur est un stream,
    pour un TFileStream, on peut atteindre plus de 2Go sans problème
    pour un TMemoryStream, c'est impossible la Capacity reste en Longint malgré que Position ou Size soit en NativeInt (int ou __int64 selon compilateur, ce qui donne 2Gio ou 8Eio)
    A savoir que Capacity grossi 8Ko par allocation pour éviter des réallocations par WriteBuffer

    Citation Envoyé par Argol_Medusa Voir le message
    J'essaye tout de même, la limitation de 32K ne semble pas être effective, car j'arrive à traiter de grosses quantitées de données qui dépassent largement les 32K, première surprise, je ne trouve pas l'overload de la fonction en __int64
    Evidemment puisque cette limite de 32Kio n'existe pas !
    Et pas d'overload, tu confonds les fonctions utilisant un buffer et celle utilisant un TStream

    Citation Envoyé par Argol_Medusa Voir le message
    Le projet ifs.exe a déclanché la classe d'exception EOutOfMemory avec le message "mémoire insuffisante'
    Pense que ton processus étant limité à 2Go, si tu a plusieurs TStream en même temps, ils occuppent tous de la mémoire, donc c'est le cumul des buffers qui te limitera !
    On a déjà parlé, pour ma part, je n'arrives pas à allouer plus de 1980Mio dans un programme, on est même pas à 2 Gio = 2048 Mio par ce qu'il doit y avoir la VCL qui bouffe la mémoire aussi

    Donc pense à libérer tes objets entre deux lectures !

    tu peux aussi lire le fichier manuellement avec le TFileStream par paquet de 60Kio au lieu de le charger le complètement puis de le recopier complètement,
    Aide via F1 - FAQ - Guide du développeur Delphi devant un problème - Pensez-y !
    Attention Troll Méchant !
    "Quand un homme a faim, mieux vaut lui apprendre à pêcher que de lui donner un poisson" Confucius
    Mieux vaut se taire et paraître idiot, Que l'ouvrir et de le confirmer !
    L'ignorance n'excuse pas la médiocrité !

    L'expérience, c'est le nom que chacun donne à ses erreurs. (Oscar Wilde)
    Il faut avoir le courage de se tromper et d'apprendre de ses erreurs

  3. #3
    Membre actif Avatar de Argol_Medusa
    Homme Profil pro Yann
    Ingénieur Radiofréquences
    Inscrit en
    août 2005
    Messages
    195
    Détails du profil
    Informations personnelles :
    Nom : Homme Yann
    Localisation : France

    Informations professionnelles :
    Activité : Ingénieur Radiofréquences
    Secteur : High Tech - Électronique et micro-électronique

    Informations forums :
    Inscription : août 2005
    Messages : 195
    Points : 185
    Points
    185

    Par défaut

    Bonjour Shai, et merci pour ton aide.


    Citation Envoyé par ShaiLeTroll Voir le message
    ton test est-il dans la même appli, même contexte, une fuite mémoire dans le projet réel ?
    Oui effectivement mon test sorti du contexte (ds autre appli séparée) c'est pas très pertinent en effet, j'ai refais le test dans le même programme :

    - quand j'ajoute en TMemoryStream je peux monter à 200Mo environ de mémoire vive (après ça crash), et si j'essaye de créer mon char de 400Mo, ça me jette aussi.
    - quand j'essaye d'abord le tableau de char, je monte à 400 Mo sans problème, je commence à ajouter des données dans le memorystream et ça me jette au bout de 200Mo de memorystream ( soit 600Mo au total dans la mémoire vive )

    Je vais suivre la piste de la fuite mémoire, l'hypothèse me semble très probable en effet !


    Citation Envoyé par ShaiLeTroll Voir le message
    En plus, tu parle de 64Bits, mais c'est l'adresse d'un pointeur ou la taille d'un fichier, ce n'est qu'une valeur, cela n'améliore pas les performances d'être en 32 ou 64 bits, cela permet d'adresse plus de mémoire certe mais cela ne change pas les calculs surtout pour un TStream

    J'ai l'impression que tu confonds la taille des registres dont découlent les possibilités d'adressage avec la puissance de calcul ?!
    Alors, en fait je parle bien de 2 choses distinctes mais mon explication n'était pas très clair

    l'idée de base était :

    - de charger jusqu'à 5 Go de données à partir du HDD,
    - de les traiter ensuite en __int64 (traitement en pointeurs cette fois-ci, rien à voir donc avec la taille HDD) pour que les traitements soient plus rapides

    Sauf qu'effectivement (bien vu j'avais oublié) windows est incapable de gérer plus de 2 ou 3 Go à priori donc je dois m'attendre soit à un refus d'allocation mémoire soit à un swap qui va me ralentir, dans tous les cas ça me met mon architecture dans l'eau.

    Il va falloir que je revois ma copie ( ou trouver autre version de windows ? )
    D'autant plus que d'après ce que tu me dis le traitement de la bibliothèque du C++ Builder n'est pas vraiment du 64 bits mais du 2x32bits


    Citation Envoyé par ShaiLeTroll Voir le message
    32K ??? D'où te viens c'est limite ???
    32bit donne une limite de 2^31 = 2Gio !
    La limite vient de mon cerveau qui était fatigué hier aprèm
    Je voyais une limite du int count dans : void __fastcall WriteBuffer(const void *Buffer, int Count);

    mais c'est un int (=long int sur PC) et pas un short int autant pour moi (c'est ça de basculer entre du µControleur 16bits et du PC à longueur de journée !!) la limite est donc bien de (2^32)/2 =2^31 = 2Go tu as parfaitement raison, et ça explique aussi pourquoi je pouvais charger sans problème des fichiers de plus de 32K (évidement).



    Citation Envoyé par ShaiLeTroll Voir le message
    Tu devrais lire le code du ReadBuffer ou WriteBuffer, il utilise Read ou Write, en général, cela lit le buffer d'un seul coup !
    Mais ils ont prévu pour des Stream que nous pourrions développer qui forcerait une limitation de buffer, d'où la boucle sur Stream en utilisant Read ou Write jusqu'à ce que la quantité de données souhaitée soit lues ou écrites
    Tu veux dire dans le cas où on voudrait créer notre propre classe dérivée d'un TStream?
    Je n'arrivais pas à voir le code associé au classes.hpp (classes.cpp), le F7 m'emmène directement en assembleur, mais en fait après recherche dans les répertoires c'est dans classes.PAS, c'est du delphi ceci explique cela.
    Je vais regarder un peu leur code.


    Citation Envoyé par ShaiLeTroll Voir le message
    Idem pour ReadBuffer ou WriteBuffer, il y a la même limite, j'ignore si le compilateur 64Bits permet de dépasser une variable de 2Go mais C++Builder est encore 32 bits !? XE3 n'apporte pas encore le 64 Bits en C++ ? Je croyais que c'est pour la prochaine version ?!
    J'avais posé la question sur leur page Youtube, mais pas eu de réponse, c'était juste avant la sortie du XE3.

    J'avais lu à l'époque qu'ils avaient fait machine arrière sur le XE3 qui devait être un 64bits mais finalement ce n'est à priori pas le cas.
    A vérifier.

    Citation Envoyé par ShaiLeTroll Voir le message
    Pour CopyFrom, cela supporte le 64bits parce que le receveur est un stream,
    pour un TFileStream, on peut atteindre plus de 2Go sans problème
    pour un TMemoryStream, c'est impossible la Capacity reste en Longint malgré que Position ou Size soit en NativeInt (int ou __int64 selon compilateur, ce qui donne 2Gio ou 8Eio)
    A savoir que Capacity grossi 8Ko par allocation pour éviter des réallocations par WriteBuffer
    ...
    Pense que ton processus étant limité à 2Go, si tu a plusieurs TStream en même temps, ils occuppent tous de la mémoire, donc c'est le cumul des buffers qui te limitera !
    Effectivement Capacity est en int sur le TMemoryStream

    Bref, tout cela doit nous amène à une limitation autour de 2Go au total effectivement, mais ça n'explique pas pourquoi ma limitation est autour de 200Mo en mémoire, ton hypothèse de fuite mémoire me plait assez, je vais re-regarder mon code

    Sinon concernant l'architecture globale, je vais devoir découper pour faire du traitement par petit packets (genre 100Mo) je pense.

    En tout cas merci bien pour tes réponses détaillées qui m'ont bien éclairées !!
    Désolé, on savait pas que c'était impossible, alors on l'a fait

  4. #4
    Membre actif Avatar de Argol_Medusa
    Homme Profil pro Yann
    Ingénieur Radiofréquences
    Inscrit en
    août 2005
    Messages
    195
    Détails du profil
    Informations personnelles :
    Nom : Homme Yann
    Localisation : France

    Informations professionnelles :
    Activité : Ingénieur Radiofréquences
    Secteur : High Tech - Électronique et micro-électronique

    Informations forums :
    Inscription : août 2005
    Messages : 195
    Points : 185
    Points
    185

    Par défaut

    Désolé pour le double post, je rajoute juste le code d'un test tout simple à partir de zéro : je clique sur le button1 pour ajouter des données (fichier de 40Mo) ça me claque à 500Mo de données cette fois-ci.

    Mais il me semble voir par moment des montées brèves à 700Mo(dans le gestionnaire des taches) lors de l'ajout du même fichier.

    Est-ce que la commande CopyFrom (oui j'ai remplacé la lecture directe par un copyfrom du coup ) n'utiliserait pas un buffer temporaire ou quelque chose qui ferait monter la mémoire ??

    Code :
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    //---------------------------------------------------------------------------
     
    #include <vcl.h>
    #pragma hdrstop
     
    #include "Unit1.h"
    //---------------------------------------------------------------------------
    #pragma package(smart_init)
    #pragma resource "*.dfm"
    TForm1 *Form1;
     
    TMemoryStream *BigBuffer;
     
    //---------------------------------------------------------------------------
    __fastcall TForm1::TForm1(TComponent* Owner)	: TForm(Owner)
    {
    }
    //---------------------------------------------------------------------------
    void __fastcall TForm1::Button1Click(TObject *Sender)
    {
    		// copier ce fichier à la suite dans le bigbuffer
    		TMemoryStream *a;
    		a = new TMemoryStream();
    		a->LoadFromFile("c:\\del\\Copier coller sous C++Builder.avi");
    		BigBuffer->CopyFrom(a,a->Size);
    		a->Clear();
    		delete a;
    		a=NULL;
    }
    //---------------------------------------------------------------------------
    void __fastcall TForm1::FormCreate(TObject *Sender)
    {
     BigBuffer = new TMemoryStream();
    }
    //---------------------------------------------------------------------------
    void __fastcall TForm1::FormClose(TObject *Sender, TCloseAction &Action)
    {
    	delete BigBuffer;
    	BigBuffer=NULL;
    }
    //---------------------------------------------------------------------------
    void __fastcall TForm1::Button2Click(TObject *Sender)
    {
    this->Button2->Caption=BigBuffer->Size;
    }
    //---------------------------------------------------------------------------
    Désolé, on savait pas que c'était impossible, alors on l'a fait

Liens sociaux

Règles de messages

  • Vous ne pouvez pas créer de nouvelles discussions
  • Vous ne pouvez pas envoyer des réponses
  • Vous ne pouvez pas envoyer des pièces jointes
  • Vous ne pouvez pas modifier vos messages
  •