Arrays dinámicos: SetLength vs GetMem/FreeMem
#1
Posted 21 October 2010 - 11:50 AM
Yo aquí ando con una duda filosófica (para variar ). ¿Que es más eficiente y rápido para trabajar con arrays dinámicos: SetLength() u optar por el enfoque hacia punteros y utilizar GetMem y FreeMem para reservar la memoria? ¿Existe alguna diferencia entre SetLength y los xxxMem?
Se que la pega de utilizar GetMem y FreeMem me lleva a pensar en punteros y adaptar mis algoritmos, pero en fin es una pregunta válida.
Esto me lo pregunto a fin de evaluar si es posible mejorar aún más mis cálculos. Si bien estoy contento con la rapidez con la que ha realizado algunas de las operaciones y que ya estuve optimizando me preguntaba si para grandes vectores y matrices (del orden de miles de elementos) habría alguna diferencia significativa.
Yo vengo trabajando con SetLength() y noto que se demora al reservar y cargar los datos iniciales... de allí en más mis algoritmos son bastante rápidos (en general). Me estuve pensando si es posible mejorar eso de la reserva y carga...
He intentado buscar algo como "How to work efficiently with large arrays" o algo por el estilo y no encuentro algo más o menos serio como para ilustrarme. Entre mis fuentes está Efg's Computer Lab que en la sección Tips and Tricks de su artículo Math Info - parte C expone algunos ejemplos basados en el uso de GetMem/FreeMem aunque dicha documentación hace referencia a una manera de trabajar con arrays de forma dinámica antes de la introducción de los open arrays en D4. De allí en más mis búsquedas en la web me llevan a cosas elementales y todas hacen referencia al uso de SetLength().
¿Alguien sabría asesorarme o darme alguna pauta?
Saludos,
#2
Posted 21 October 2010 - 04:08 PM
GetMem es para nuevos arreglos y SetLength si se desea aumentar el tamaño de un arreglo existente.
Yo creo que comparas peras y manzanas. No son para lo mismo.
Saludos
#3
Posted 21 October 2010 - 04:36 PM
Te agradezco que me hayas dado esa aclaración. La verdad es que no lo veía demasiado claro cuando leía la propia ayuda de Delphi, mi inglés no es del todo bueno.
Yo ya estoy un tanto confundido... en la ayuda sobre el tema de los arrays abiertos indica que ha de emplearse SetLength() para reservar la memoria e indicar el tamaño del arreglo. No menciona nada del hecho que deba emplearse GetMem.
La gran mayoría de los ejemplos que estuve viendo y del material que he estado consultando habla de SetLength() para trabajar con matrices, sea para indicar la memoria para comenzar a trabajar como si se desease redimensionarlo.
La poca referencia sobre el uso de arreglos y GetMem/FreeMem data de antes de D4 y menciona que se lo utilizaba para redimensionar los arreglos. A partir de D4 es que existen los arreglos dinámicos.
Pero recuerdo haber visto en una biblioteca de matemática que había probado que también utilizaba esta técnica.
Por ello es que me ando preguntando como sería adecuado estar trabajando... Yo siempre he utilizado SetLength() aunque en la última semana me he estado preguntando si es que habrá algo más eficiente para trabajar con las matrices, reservar memoria y redimensionar.
En mi código poco tengo que estar cambiando el tamaño a mis vectores y matrices. Una vez dado el tamaño la gran mayoría de las veces quedan así hasta su liberación.
Pero como dije, noté que la carga de datos ni bien se dimensiona es lenta a comparación con el resto de los cálculos.
Es así que recordé el uso de GetMem/FreeMem para reservar memoria e inició mi duda.
Saludos,
#4
Posted 21 October 2010 - 05:06 PM
MyTipo *T = malloc(sizeof(MyTipo)*N_elementos); // reservo memoria MyTipo Uno = T[27] // Acceso por índice: es el elemento 27 siendo 0 el primero MyTipo Dos = *(T+27) // Acceso por aritmética de punteros: También es el elemento 27
Como se ve el acceso por índice es mas claro sintácticamente, sin embargo la aritmética de punteros es algo más rápida. Cuando quiero algo mas de velocidad uso la aritmética de punteros.
Pienso, pero debería comprobarlo, que en delphi sucede algo parecido. Para verlo claramente se puede ver el código ensamblador generado, este tipo de ejercicio suele ser bastante esclarecedor.
Saludos.
#5
Posted 21 October 2010 - 05:21 PM
Entonces... ¿tu sospechas que en caso de poder tratarse con punteros sobre vectores y matrices con Delphi sería más o menos parecido a C y que podría ser más rápido?
Saludos,
#6
Posted 22 October 2010 - 06:53 AM
Hola escafandra,
Entonces... ¿tu sospechas que en caso de poder tratarse con punteros sobre vectores y matrices con Delphi sería más o menos parecido a C y que podría ser más rápido?
Saludos,
Exacto, si tengo un rato miro el código asm generado, con esto las sospechas estarán mejor fundadas. La única pega que puede tener en delphi es que la aritmética de punteros es mas engorrosa que en C. Me explico. Si quiero un elemento pongo *(T+27) y me da el elemento 27, es decir al puntero le sumo 27. Pero realmente al puntero se le suma 27*sizeof(MyTipo) directamente. En delphi debes pasar el puntero a un DWORD, le añades lo que quieres y realizas el cast a puntero nuevamente: PMyTipo(DWORD(T)+(27*sizeof(MyTipo))) Esto es lo que puede plantear la duda de eficiencia. Por eso creo que lo mejor es ver como queda una vez compilado.
Saludos
#7
Posted 22 October 2010 - 07:46 AM
Exacto, si tengo un rato miro el código asm generado, con esto las sospechas estarán mejor fundadas. La única pega que puede tener en delphi es que la aritmética de punteros es mas engorrosa que en C. Me explico. Si quiero un elemento pongo *(T+27) y me da el elemento 27, es decir al puntero le sumo 27. Pero realmente al puntero se le suma 27*sizeof(MyTipo) directamente. En delphi debes pasar el puntero a un DWORD, le añades lo que quieres y realizas el cast a puntero nuevamente: PMyTipo(DWORD(T)+(27*sizeof(MyTipo))) Esto es lo que puede plantear la duda de eficiencia. Por eso creo que lo mejor es ver como queda una vez compilado.
Saludos
De que puede resultar más engorroso te lo creo... anoche intenté hacer unas pruebas con GetMem/FreeMem sobre un array como el siguiente:
type TMatrix = array of array of double; PMatrix = ^TMatrix; var Matrix: PMatrix;
Y no podía lograr que funcionase. Errores por todos lados . Ya me estoy inclinando a no estropearme la cabeza y directamente emplear SetLength. Que después de todo a pesar de que tiene sus demoras para reservar la memoria es bastante rápido considerando las operaciones a la que se verá sometido... Con una de las matrices más grande que empleo (aprox. 10304 x 40 de tipo double que vendría a ser unos 3220 KB) y basándome en GetTickCount (si... ya que no es de lo más preciso... pero es que olvidé del nombre y como se emplea QueryPerfomanceFrecuency) lo más lento que ha llegado a reservar memoria y hacer unas asignaciones fue de 4 segundos y lo más rápido fue cerca a medio segundo. Tengo que admitir que tampoco es que dejaba descansar al sistema y que terminase de liberar los 3 megas...
Estúpidamente se me olvida de medir el rendimiento de la PC con algo más serio y preciso... yo por flojera abrí el Administrador de Tareas y no veía una pérdida considerable... ahora estuve buscando en mi equipo una especie de "Adminsitrador de Tareas" mejorado que recuerdo haberlo descargado desde el mismísimo sitio de Microsoft y no lo encuentro. ¿Alguno sabe por casualidad el nombre de a lo que me refiero para comprobar si lo tengo o si es que estúpidamente lo eliminé?
Saludos,
#8
Posted 22 October 2010 - 08:51 AM
Ahora de lo otro... no se para donde apuntar
Saludos,
#9
Posted 22 October 2010 - 08:54 AM
#10
Posted 22 October 2010 - 09:20 AM
Utilizaría ese programa... si supiera de Linux. Al menos lo tendré como nota para cuando de el paso hacia la luz y deje el lado oscuro.Yo en linux uso http://valgrind.org/ para eso. Es una especie de máquina virtual donde se ejecuta el programa y da muy buenos datos sobre memory leaks, memoria total usada y muchas más cosas. Veo que no funciona en Windows. Suerte con eso
Gracias por el aliento.
Saludos,
#11
Posted 22 October 2010 - 03:42 PM
Un array está representado por un puntero al primer elemento del mismo.
Un Array de dos dimensiones es un array de arrays, es decir se representa por un puntero a punteros. Cada uno de esos punteros es un array.
De forma que un array de dos dimensiones tiene una imagen en memoria de una serie de punteros y cada uno de ellos apunta a un array del tipo que queremos.
Quizás el código clarifique las cosas:
var Matrix: array of array of double; a,b: integer; n: cardinal; begin // El array lo definimos como a por b : Matrix[a, b]; a:= 2; // primera dimensión b:= 3; // Segunda dimensión // Localizamos la memoria: // Necesitamos a punteros para que apunten a cada array de double + a*b doubles GetMem(Matrix, sizeof(Pointer)*a + a*b*sizeof(double)); // Asignamos los punteros. Esto puede ser un poco lioso. // PPointer(cardinal(Matrix) + n*sizeof(Pointer)) representa a cada puntero a punteros: dimensión primera // Pointer(cardinal(Matrix)+sizeof(Pointer)*a) representa donde debe apuntar el primer puntero a puntero. Su contenido es el elemento Matrix[0,0] // Asi, Pointer(cardinal(Matrix)+sizeof(Pointer)*a + b*sizeof(double)*n); son los punteros o arrais de la segunda dimensión for n:=0 to a-1 do begin PPointer(cardinal(Matrix) + n*sizeof(Pointer))^:= Pointer(cardinal(Matrix)+sizeof(Pointer)*a + b*sizeof(double)*n); end; // Ahora podemos acceder por índices o por punteros. Matrix[0, 0] := 0; // También lo localizamos así: Double(PPointer(Matrix)^^) Matrix[0, 1] := 1; Matrix[0, 2] := 2; Matrix[1, 0] := 10; Matrix[1, 1] := 11; Matrix[1, 2] := 12; end;
Espero haberme explicado con claridad
Saludos.
#12
Posted 22 October 2010 - 05:11 PM
¡Gracias escafandra por traer luz a este completo ignorante! Yo estaba intentando algo diferente.
Primero que nada, en vez de pasar una variable de tipo Matrix a GetMem, yo pasaba una variable de tipo puntero a Matrix. Es decir:
type PMatrix = ^TMatrix; ... var ptMatrix: PMatrix; ... GetMem(ptMatrix, ....);
En segundo lugar no tuve en cuenta que la variable apunta a un puntero donde está la verdadera matriz y que debía sumarle, e intentaba pedir memoria basado en el tipo Matriz yo asumí directamente que era posible algo como esto:
GetMem(ptMatrix, SizeOf(Matrix) * M);
Y luego que debería pedir tamaño para cada fila:
for i := 0 to N-1 do GetMem(ptMatrix^[i], SizeOf(double) * N);
Y que luego para acceder a los datos podría ser algo como:
ptMatrix^[i]^[j] := i + j;
Luego tras algunas advertencias probé algunas variaciones menores hasta que logré compilar (no me pregunten como ... me dije ¡lo logré!) pero recibí algunos errores de acceso a memoria en tiempo de ejecución.
Como no me gustó el sabor a derrota, cerré el IDE sin guardar el proyecto de prueba y no volví a intentarlo.
Estuve leyendo para ver si encontraba algo que me clarifique las cosas pero no entra nada en esta cabeza... y eso que está hueca y vacía.
Voy a estudiar tus explicaciones con mayor detenimiento. Voy entendiendo mejor gracias a tus palabras; pondré en práctica lo que comentas con mayor tranquilidad mañana. Ahorita mi cabeza está preocupada en lo que hace a documentación.
Nuevamente gracias. Luego pondré algunas comparaciones sobre ambas técnicas.
Saludos,
#13
Posted 23 October 2010 - 03:24 AM
Aunque en el ejemplo localizo toda la memoria de golpe para toda la matriz (creo que es mas rápido y eficiente) puedes hacerlo por partes como pretendías. Es decir, creas espacio para los n punteros que representan la dimensión primera y después, en el bucle de asignación de punteros, creas el espacio para cada matriz del tamaño de la segunda dimensión. Quizás esta segunda forma es más fácil de entender pero multiplica las llamadas a GetMem y obliga a liberar la memoria de la misma forma. Otro efecto es que dicha memoria no es consecutiva.
Es mas rápido localizar la memoria de golpe y su liberación también lo es, requiriendo una sola llamada a FreeMem.
Tu decides como localizar la memoria
Por otro lado el acceso a los elementos puedes hacerlo por índice o por punteros, una vez entendido como funciona el asunto. Piensa que en realidad en cualquier matriz puedes acceder con punteros directamente independientemente de como asignaste la memoria, si por SetLength o por el método que he expuesto.
Saludos.
#14
Posted 23 October 2010 - 06:05 AM
Gracias por tus consejos. Ahora que comentas de que se llamaría a GetMem varias veces... y recordando lo que antes me había dicho jorgeu creo que la única opción es pedir toda la memoria de una como tu haces porque de otro modo estaría reservando y liberando memoria en cada iteración con lo que se me hace que podría estar perdiendo algunas referencias.
En la tarde estaré más tranquilo y por fin haré pruebas , ahorita quiero terminar el tema de la documentación que me ha quedado a medias ayer. Muy posiblemente el uso de GetMem y FreeeMem tenga cierta ventaja para el caso de mis matrices que no necesitan estar redimensionandose. Según mis procedimientos casi ninguna necesita estar cambiando de tamaño... es más hasta me atrevería a decir que ninguna lo necesitaría.
Saludos,
#15
Posted 23 October 2010 - 02:12 PM
Actualizo el hilo. He estado codificando un sistema a modo de prueba. Y hasta el momento solo he estado probando el método tradicional (SetLength) consiguiendo unos resultados bastante optimistas, pero no quisiera aventurarme demasiado.
Luego he estado llevando la codificación con el método GetMem/FreeMem. Al probar mi código algo falla. Recibo unos avisos de error al finalizar el programa, primero un AccessViolation en la dirección 00C4FFFC y finalmente un Runtime Error 216 at 004034BA.
Esto es cuando cierro el programa e intento correr la prueba basada en GetMem. Con el método tradicional no hay problemas.
La verdad es que ando desconcertado... porque poniendo breackpoint en mi código todo parece andar perfecto.
Primeramente con una traza había detectado que la falla estaba en el Finalize(), lo comenté y dejé en FreeMem para comprobar si pasaba por allí la cosa y así fue... se ejecutaba con normalidad FreeMem. Con eso debería bastar... pero no: el error al salir sigue.
Es justo al momento de salir... en principio el error me debería estar indicando que hay un intento de hacer uso de algún objeto no instanciado o ya liberado pero no hago uso de ningún objeto, variable o lo que fuese ni de los eventos OnClose o OnCloseQuery...
¿Que puede ser? :'(
Aquí dejo el code:
unit Unit1; interface uses Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms, Dialogs, StdCtrls; const // Tamaño de las matrices Rows: integer = 92 * 112; //10304 Cols: Integer = 40; // Precisión del reloj: 1/10000 seg. // Idea y forma de cálculo basada en: // [url]http://www.marteens.com/trick4c.htm[/url] // En teoría es lo más preciso PrecisionCounter = 10000; type // Nuestro tipo para matrices // 10304 * 40 * 8 = 3297280 bits // = 3220 Kb // aprox. 3,1445 Mb TMatrix = array of array of Double; TForm1 = class(TForm) Button1: TButton; Button2: TButton; Label1: TLabel; Label2: TLabel; procedure Button1Click(Sender: TObject); procedure Button2Click(Sender: TObject); procedure FormCreate(Sender: TObject); private { Private declarations } public { Public declarations } end; var Form1: TForm1; Matrix1, Matrix2: TMatrix; Start, Finish, Freq: Int64; implementation {$R *.dfm} procedure TForm1.Button1Click(Sender: TObject); var i,j: Integer; begin // Aquí emplearemos el método "tradicional" // Activamos "cronómetro" QueryPerformanceCounter(Start); //Aqui el algoritmo: SetLength(Matrix1,Rows,Cols); // Método seguro. Si bien puede usarse // for <variable> := 0 to <valor-max> - 1 do // es conveniente el uso de Low() y High() for i := Low(Matrix1) to High(Matrix1) do for j := Low(Matrix1[1]) to High(Matrix1[1]) do Matrix1[i][j] := i + j; SetLength(Matrix1,0,0); // O también puede usarse Finalize() Matrix1 := nil; // preventiva // Paramos "cronómetro" QueryPerformanceCounter(Finish); Label1.Caption := '1/10000 seg: ' + FormatFloat('0,',(Finish - Start) * PrecisionCounter div Freq); end; procedure TForm1.Button2Click(Sender: TObject); var i,j: Integer; k: Cardinal; begin // Aquí emplearemos el método GetMem/FreeMem // Activamos "cronómetro" QueryPerformanceCounter(Start); //Aqui el algoritmo: GetMem(Matrix2, SizeOf(Pointer) * Rows + Rows * Cols * SizeOf(Double)); for k := 0 to Rows - 1 do PPointer(Cardinal(Matrix2) + k*SizeOf(Pointer))^:= Pointer(Cardinal(Matrix2)+SizeOf(Pointer)* Rows + Cols * SizeOf(double)* k); // Método seguro. Si bien puede usarse // for <variable> := 0 to <valor-max> - 1 do // es conveniente el uso de Low() y High() for i := Low(Matrix2) to High(Matrix2) do for j := Low(Matrix2[1]) to High(Matrix2[1]) do Matrix2[i][j] := i + j; // Según documentación, cuando se trata de arrays dinámicos // y se utiliza GetMem/FreeMem debe emplearse previamente Finalize //Finalize(Matrix2); Comentado por un error aquí! FreeMem(Matrix2, SizeOf(Pointer) * Rows + Rows * Cols * SizeOf(Double)); // Paramos "cronómetro" QueryPerformanceCounter(Finish); Label2.Caption := '1/10000 seg: ' + FormatFloat('0,',(Finish - Start) * PrecisionCounter div Freq); end; procedure TForm1.FormCreate(Sender: TObject); begin // Calculamos la frecuencia a la que corre el reloj QueryPerformanceFrequency(freq); end; end.
Como ven... no hay demasiadas cosas ocultas. Ojalá alguien me pudiera hechar un cable y ver donde está el problema.
Esto me está terminando de convencer que directamente evite todo uso de GetMem, acceso a memoria, los punteros, etc. Les comento los resultados que he obtenido tras 5 pruebas con el método tradicional:
+--------------------+-----+ |Tiempo: 1/10000 seg | CPU | +--------------------+-----+ | //Antes: | 7% | +--------------------+-----+ | 461 | 10% | +--------------------+-----+ | 462 | 10% | +--------------------+-----+ | 875 | 13% | +--------------------+-----+ | 471 | 11% | +--------------------+-----+ | 463 | 16% | +--------------------+-----+ | Promedio: 546,4 | 12% | +--------------------+-----+
Considerando que los valores indicados en las constantes son representativos a una de las matrices de mayor dimensión con la que trabajo, y viendo estos números entonces podría llegarse a la conclusión de que SetLength cumple con su propósito de una manera bastante óptima y aceptable.
El uso de la memoria física y virtual no ha variado significativamente, se ha mantenido en los niveles iniciales. Sólo ha llegado a "disparar" los indicadores de CPU y de I/O pero en unos valores se normalizan en un instante. Entre cada prueba se han esperado en promedio minuto y medio.
No ha estado ejecutando ninguna aplicación más salvo la prueba, el Process Explorer de Microsoft y el Antivirus en segundo plano.
Lo que si se ha observado es que una vez finalizada la aplicación y en la espera de una normalización de los indicadores para la prueba con GetMem se ha detectado un disparo de uso de CPU de hasta el 35%. El mismo Explorer indicaba que este empezaba a consumir los recursos. Con cerrarlo y esperar un minuto y medio se volvió a la normalidad.
Las pruebas se realizaron corriendo en una AMD Duron 1.16 GHz, 512 RAM. Si alguien más se anima a someter a prueba las condiciones en sus equipos y compartir sus resultados, le estaría enormemente agradecido.
Saludos,
#16
Posted 23 October 2010 - 06:50 PM
Si puedo hacer unas pruebas ya te comento ahora es un poco tarde para mi.
Saludos.
#17
Posted 23 October 2010 - 07:00 PM
Hola escafandra,He probado el código con las dimensiones que propones y tengo el mismo error que tu. Por lo que veo delphi no se comporta igual que el C en el manejo de los arrays. En C no se presenta ningún problema. Es posible que se me escape algún detalle en el manejo de la memoria como uso de alguna posición para guardar tamaños como ocurre con los String. En C el array no guarda su tamaño.
Si puedo hacer unas pruebas ya te comento ahora es un poco tarde para mi.
Saludos.
No hay problemas, no es obligación ni te sientas presionado amigo.
No sabría decir hasta que punto podría ser problema de la liberación de memoria... cuando el breakpoint pasa por FreeMem me indica que efectivamente, se ha liberado la memoria y continúa con las siguientes instrucciones.
Estuve buscando sobre el error 216 pero por ahora nada que indique en concreto a que se debe el error. La mayoría de los sitios que hablan de esto dan a entender que no existe un caso en particular en el que se produce el error sino que es un caso un tanto general que las situaciones que derivan en ese error pueden ser variadas.... Ninguno ofrece una solución, pareciera no haber algo bien concreto sino que debe estudiarse caso a caso.
Saludos,
#18
Posted 24 October 2010 - 11:58 AM
Usando un truco que en ocasiones he utilizado para tratar los buffer como arrays en delphi (en C es automático):
type AMatrix = array [0..0] of array of Double; PMatrix = ^AMatrix;
Si lo usas así tienes el problema de que no puedes acceder a valores de la matriz con indices que sean constantes pues el compilador protesta, aunque con variables no tienes problemas.
Otra forma es usar:
type TMatrix = array of array of Double; PMatrix = ^TMatrix;
Pero entonces tienes que acceder a la matriz como
TMatrix(Matrix2)[a][b];
Cualquiera de las dos formas funciona bien.
He usado la API VirtualAlloc y VirtualFree para acceder directamente al S.O. y el resultado es apabullante a favor del bajo nivel 2,5 veces más rápido sin usar aritmética de punteros. Y, además, el error que mencionabas está solucionado.
Coloco tu código modificado por mi sistema:
unit Unit1; interface uses Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms, Dialogs, StdCtrls; const // Tamaño de las matrices Rows: integer = 92 * 112; //10304 Cols: Integer = 40; // Precisión del reloj: 1/10000 seg. // Idea y forma de cálculo basada en: // [url]http://www.marteens.com/trick4c.htm[/url] // En teoría es lo más preciso PrecisionCounter = 10000; type // Nuestro tipo para matrices // 10304 * 40 * 8 = 3297280 bits // = 3220 Kb // aprox. 3,1445 Mb TMatrix = array of array of Double; AMatrix = array [0..0] of array of Double; PMatrix = ^TMatrix; TForm1 = class(TForm) Button1: TButton; Button2: TButton; Label1: TLabel; Label2: TLabel; procedure Button1Click(Sender: TObject); procedure Button2Click(Sender: TObject); procedure FormCreate(Sender: TObject); private { Private declarations } public { Public declarations } end; var Form1: TForm1; Matrix1: TMatrix; Matrix2: PMatrix; Start, Finish, Freq: Int64; implementation {$R *.dfm} procedure TForm1.Button1Click(Sender: TObject); var i,j: Integer; begin // Aquí emplearemos el método "tradicional" // Activamos "cronómetro" QueryPerformanceCounter(Start); //Aqui el algoritmo: SetLength(Matrix1,Rows,Cols); // Método seguro. Si bien puede usarse // for <variable> := 0 to <valor-max> - 1 do // es conveniente el uso de Low() y High() for i := Low(Matrix1) to High(Matrix1) do for j := Low(Matrix1[1]) to High(Matrix1[1]) do Matrix1[i][j] := i + j; SetLength(Matrix1,0,0); // O también puede usarse Finalize() Matrix1 := nil; // preventiva // Paramos "cronómetro" QueryPerformanceCounter(Finish); Label1.Caption := '1/10000 seg: ' + FormatFloat('0,',(Finish - Start) * PrecisionCounter div Freq); end; procedure TForm1.Button2Click(Sender: TObject); var i,j: Integer; k: Cardinal; begin // Aquí emplearemos el método GetMem/FreeMem // Activamos "cronómetro" QueryPerformanceCounter(Start); //Aqui el algoritmo: Matrix2:= VirtualAlloc(nil, SizeOf(Pointer) * Rows + Rows * Cols * SizeOf(Double), MEM_COMMIT, PAGE_READWRITE); for k := 0 to Rows - 1 do PPointer(Cardinal(Matrix2) + k*SizeOf(Pointer))^:= Pointer(Cardinal(Matrix2)+SizeOf(Pointer)* Rows + Cols * SizeOf(double)* k); for i := 0 to Rows-1 do for j := 0 to Cols-1 do TMatrix(Matrix2)[i][j] := i + j; VirtualFree(Matrix2, 0, MEM_RELEASE); // Paramos "cronómetro" QueryPerformanceCounter(Finish); Label2.Caption := '1/10000 seg: ' + FormatFloat('0,',(Finish - Start) * PrecisionCounter div Freq); end; procedure TForm1.FormCreate(Sender: TObject); begin // Calculamos la frecuencia a la que corre el reloj QueryPerformanceFrequency(freq); end; end.
Realiza pruebas y nos comentas.
Saludos.
#19
Posted 24 October 2010 - 01:36 PM
¡Gracias por el dataso!
Me pondré a hacer pruebas.
Hay cosas que no entiendo mucho, de ese "truco" y del uso de VirtualAlloc y VirtualFree. Tendré que documentarme mejor sobre eso.
He visto en código que tu, seoane y javier exponen en más de una ocasión declaración de arrays de la forma:
TArray = array [0..0] of algo;
Tengo entendido que esa es una técnica de generar arrays dinámicos pero... ¿Que se gana o que ventaja ofrece respecto a la construcción típica?:
TArray = array of array of algo;
Como en mi ejemplo.
Desconocía VirtualAlloc/VirtualFree. ¿Que de diferente es respecto a GetMem/FreeMem? Leo en el enlace que ofreces que Virtualxxx trabaja con un espacio de la memoria virtual ¿Que acaso GetMem/FreeMem no hacen lo mismo?
Me pondré a hacer algunas pruebas. Aunque ya me adelantaste algunas cosillas...
Si es 2,5 veces más rápido y viendo que dentro de todo es bastante amigable el código creo que es una buena opción a considerar.
Una última preguntita... que no me logro aclarar del todo mientras estoy leyendo el artículo en MSDN... ¿Y si tuviera que redimendionar?
Disculpas mis preguntas. Este manejo es nuevo para mi. Hace tiempo que me desacostumbré a los punteros... la culpa la tienen mis profes que no nos "obligaron" a estudiar con mayor profundidad y a tenerle más respeto al trabajo "orientado a punteros". Recuerdo que la última vez que trabajé con punteros en cátedra fue cuando vimos el tema de árboles.
Saludos,
#20
Posted 24 October 2010 - 03:04 PM
He visto en código que tu, seoane y javier exponen en más de una ocasión declaración de arrays de la forma:
delphi
TArray = array [0..0] of algo;
Tengo entendido que esa es una técnica de generar arrays dinámicos pero... ¿Que se gana o que ventaja ofrece respecto a la construcción típica?:
Bueno, en esta ocasión la uso para poder declarar un puntero a ese tipo y asignarle la memoria localizada o cualquier buffer, así podemos tratar ese bloque como una matriz. Dicho de otra forma, hacemos un casting.
Existen muchas formas de asignar bloques de memoria en la API. Cualquier función que trate de asignar memoria desde un lenguaje de programación al final terminan llamando a la API tras mas o menos tareas y comprobaciones, así que ¿Porque no llamar directamente a la API?.Desconocía VirtualAlloc/VirtualFree. ¿Que de diferente es respecto a GetMem/FreeMem? Leo en el enlace que ofreces que Virtualxxx trabaja con un espacio de la memoria virtual ¿Que acaso GetMem/FreeMem no hacen lo mismo?
Pues este es uno de los problemas que podemos tener. En un array de este tipo deberemos asignar toda la memoria de nuevo mas/menos el espacio nuevo, copiar los datos y destruir el bloque antiguo.Una última preguntita... que no me logro aclarar del todo mientras estoy leyendo el artículo en MSDN... ¿Y si tuviera que redimendionar?
Disculpas mis preguntas. Este manejo es nuevo para mi. Hace tiempo que me desacostumbré a los punteros...
Bueno, es un tema que puede resultar un poco engorroso. Algunos lenguajes como el C, están muy orientados a los mismos y quizás por eso son o muy queridos o muy odiados. Delphi no los maneja mal paro a mi parecer, en este asunto es mucho mas claro el C ya que no realiza casting "automáticos" y te permite hacer cualquier cosa.
Saludos.