Précédent   Forum du club des développeurs et IT Pro > C et C++ > Outils pour C & C++ > C++Builder
C++Builder Environnement de développement RAD C++Builder. Avant de poster -> F.A.Q C++Builder, Sources C++Builder
Partagez cette discussion sur d'autres réseaux sociaux : Viadeo Twitter Google Facebook Digg Delicious MySpace Yahoo
Réponse
 
Outils de la discussion
Publicité
'
Vieux 25/10/2012, 11h19   #1
Argol_Medusa
Membre habitué
 
Avatar de Argol_Medusa
 
Homme Yann
Ingénieur Radiofréquences
Inscription : août 2005
Messages : 153
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 : 153
Points : 143
Points : 143
Envoyer un message via MSN à Argol_Medusa
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 :

Citation:
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
Argol_Medusa est déconnecté   Envoyer un message privé Réponse avec citation 00
Vieux 25/10/2012, 14h46   #2
ShaiLeTroll
Expert Confirmé Sénior
 
Avatar de ShaiLeTroll
 
Homme
Développeur C++\Delphi
Inscription : juillet 2006
Messages : 9 187
Détails du profil
Informations personnelles :
Sexe : Homme
Âge : 32
Localisation : France

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

Informations forums :
Inscription : juillet 2006
Messages : 9 187
Points : 13 233
Points : 13 233
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

Halte à la ségrégation des Cinémas, VO sur Paris, VF en Banlieue, Abonnement résilié !
ShaiLeTroll est déconnecté   Envoyer un message privé Réponse avec citation 00
Vieux 26/10/2012, 11h06   #3
Argol_Medusa
Membre habitué
 
Avatar de Argol_Medusa
 
Homme Yann
Ingénieur Radiofréquences
Inscription : août 2005
Messages : 153
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 : 153
Points : 143
Points : 143
Envoyer un message via MSN à Argol_Medusa
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
Argol_Medusa est déconnecté   Envoyer un message privé Réponse avec citation 00
Vieux 26/10/2012, 12h01   #4
Argol_Medusa
Membre habitué
 
Avatar de Argol_Medusa
 
Homme Yann
Ingénieur Radiofréquences
Inscription : août 2005
Messages : 153
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 : 153
Points : 143
Points : 143
Envoyer un message via MSN à Argol_Medusa
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
Argol_Medusa est déconnecté   Envoyer un message privé Réponse avec citation 00
Réponse
Outils de la discussion

Navigation rapide


Fuseau horaire GMT +2. Il est actuellement 23h35.


 
 
 
 
Partenaires

Hébergement Web