Página 2 de 2
Usar objetos
Enviado: 22 Jul 2023 06:52
por marcosgambeta
JoséQuintas escreveu:E com classes/objetos ? o que acontece ?
O objeto vai ser multiplicando conforme vai passando por funções ?
...
Será que é igual array? que entra como referência automaticamente?
Internamente, arrays e objetos são a mesma coisa. Veja a definição dos tipos no arquivo hbapi.h:
Código: Selecionar todos
#define HB_IT_NIL 0x00000
#define HB_IT_POINTER 0x00001
#define HB_IT_INTEGER 0x00002
#define HB_IT_HASH 0x00004
#define HB_IT_LONG 0x00008
#define HB_IT_DOUBLE 0x00010
#define HB_IT_DATE 0x00020
#define HB_IT_TIMESTAMP 0x00040
#define HB_IT_LOGICAL 0x00080
#define HB_IT_SYMBOL 0x00100
#define HB_IT_ALIAS 0x00200
#define HB_IT_STRING 0x00400
#define HB_IT_MEMOFLAG 0x00800
#define HB_IT_MEMO (HB_IT_MEMOFLAG | HB_IT_STRING)
#define HB_IT_BLOCK 0x01000
#define HB_IT_BYREF 0x02000
#define HB_IT_MEMVAR 0x04000
#define HB_IT_ARRAY 0x08000
#define HB_IT_ENUM 0x10000
#define HB_IT_EXTREF 0x20000
#define HB_IT_DEFAULT 0x40000
#define HB_IT_RECOVER 0x80000
#define HB_IT_OBJECT HB_IT_ARRAY
#define HB_IT_NUMERIC (HB_IT_INTEGER | HB_IT_LONG | HB_IT_DOUBLE)
#define HB_IT_NUMINT (HB_IT_INTEGER | HB_IT_LONG)
#define HB_IT_DATETIME (HB_IT_DATE | HB_IT_TIMESTAMP)
#define HB_IT_ANY 0xFFFFFFFF
#define HB_IT_COMPLEX (HB_IT_BLOCK | HB_IT_ARRAY | HB_IT_HASH | HB_IT_POINTER | /* HB_IT_MEMVAR | HB_IT_ENUM | HB_IT_EXTREF |*/ HB_IT_BYREF | HB_IT_STRING)
#define HB_IT_GCITEM (HB_IT_BLOCK | HB_IT_ARRAY | HB_IT_HASH | HB_IT_POINTER | HB_IT_BYREF)
#define HB_IT_EVALITEM (HB_IT_BLOCK | HB_IT_SYMBOL)
#define HB_IT_HASHKEY (HB_IT_INTEGER | HB_IT_LONG | HB_IT_DOUBLE | HB_IT_DATE | HB_IT_TIMESTAMP | HB_IT_STRING | HB_IT_POINTER)
Note que HB_IT_OBJECT recebe o mesmo valor que HB_IT_ARRAY.
A estrutura é uma só tanto para array quanto para objeto:
Código: Selecionar todos
/* internal structure for arrays */
typedef struct _HB_BASEARRAY
{
PHB_ITEM pItems; /* pointer to the array items */
HB_SIZE nLen; /* number of items in the array */
HB_SIZE nAllocated; /* number of allocated items */
HB_USHORT uiClass; /* offset to the classes base if it is an object */
HB_USHORT uiPrevCls; /* for fixing after access super */
} HB_BASEARRAY, * PHB_BASEARRAY;
O Harbour consegue separar array de objeto graças à esta linha no arquivo hbvmpub.h:
Código: Selecionar todos
# define HB_ARRAY_OBJ( p ) ( ( p )->item.asArray.value->uiClass != 0 )
Se o valor do membro uiClass for diferente de zero, então é um objeto.
Concluindo, objetos não são duplicados, mas sim passados por referência igual a uma array.
Usar objetos
Enviado: 22 Jul 2023 10:55
por marcosgambeta
marcosgambeta escreveu:José,
Atente pare este detalhe: você está fazendo testes com strings VAZIAS.
Experimente colocar conteúdo nas strings e observe os resultados.
Inicie com valores menores e vá aumentando, observando o consumo de memória.
José,
Pode desconsiderar esta informação. Strings com espaços ou com outros caracteres, o resultado é o mesmo.
É o sistema operacional quem fornece os blocos de memória solicitados pelo Harbour. Se o sistema não puder fornecer, ocorre um erro interno no Harbour. O mesmo acontece se o Harbour solicitar um bloco de memória com 0 de tamanho.
Se não está ocorrendo nenhum erro, então a memória está sendo fornecida. Logo, a resposta está na forma como o Windows gerencia a memória.
Usar objetos
Enviado: 22 Jul 2023 14:31
por JoséQuintas
Não, porque a soma de Len() mostra que está tudo lá
Mas parece que não adiantou nem registrar como bug.
Ou tem compressão ou temos um problema grave com array que compromete tudo, afinal, não cabe na memória e só pode estar invadindo espaço
Usar objetos
Enviado: 22 Jul 2023 16:48
por JoséQuintas
Coloquei como issue no harbour, mas não entendi nada da explicação.
O cara considera normal.
Outro exemplo: preenchendo array de 10 milhões de elementos com quase 200MB.
Somando o tamanho de cada elemento após isso.
Código: Selecionar todos
20/09/2021 11:50 193.893.864 OpenOffice41.exe
#define THISTEST 0
? Memory(THISTEST)
x := Array(10000000)
AFill( x, MemoRead( "openoffice41.exe" ) )
FOR EACH i IN x
t += Len( i )
NEXT
? Transform( t, "999,999,999,999,999,999" )
? Memory(THISTEST)
2097151
1,938,938,640,000,000
2097151
Já nem sei quanto é isso, talvez perto de 2.000 terabytes.
Pra onde foi tudo isso?
Usar objetos
Enviado: 22 Jul 2023 17:40
por JoséQuintas
Alguém pode testar esse fonte em harbour e xharbour ?
Com algum arquivo grande lógico.
Quero saber qual harbour funciona, ou se o xharbour funciona melhor que o harbour.
O correto é dar erro.
Usar objetos
Enviado: 22 Jul 2023 21:43
por JoséQuintas
marcosgambeta escreveu:Note que HB_IT_OBJECT recebe o mesmo valor que HB_IT_ARRAY.
Mostramos problemas com objetos inexplicáveis por aqui.
Agora estou mostrando um problema onde o array cresce além da memória disponível.
Juntando A+B......
Tentei postar como issue, olhe a resposta:
There are no 1 million elements (strings). There is a single one string.
For better understanding TEST1. How many elements has array a?
a := ARRAY(1000000)
AFill(a, {"A"})
? a[1, 1]
? a[2, 1]
a[1, 1] := "B"
? a[1, 1]
? a[2, 1] // Why "second" element is "B" if I changed the "first" element?
For better understanding TEST2. How many allocation were made? 1? 1000000?
a := ARRAY(1000000)
AFill(a, MySpace(100000000))
FUNC MySpace(n)
? "Allocate", n
RETURN Space(n)
Como array multidimensional é óbvio que trata-se de um único array, a string não vai estar sendo duplicada.
Mas usar um pra explicar o outro.... sei lá... e fechou a issue.
Usar objetos
Enviado: 23 Jul 2023 07:35
por Jairo Maia
Outra coisa interessante:
Exemplos de comportamentos de uso de memória usando string e array:
Usando String mostra que cada
STRING usa 1 byte de memória:
Código: Selecionar todos
#include "inkey.ch"
#include "hbmemory.ch"
Function Main()
local x, cString, nOldCursor
cString := Repl( "X", 1000024 ) // cString com 1 mega...
Clear Screen
nOldCursor := SetCursor( 0 )
For x:=1 To 1000024 // vamos tentar chegar a 1 tera...
cString += cString
? ""
? ""
? ""
? "Valores em KB:"
? ""
? "Free Variable Space...:", Memory( HB_MEM_CHAR )
? "Largest String........:", Memory( HB_MEM_BLOCK )
? "RUN Memory............:", Memory( HB_MEM_RUN )
? "Virtual Memory........:", Memory( HB_MEM_VM )
? ""
? "Em x sendo ...........:", x
? "Tamanho de cString....:", Len( cString )
If Inkey() = K_ESC
Exit
EndIf
SetPos( 0, 0 )
Next
SetCursor( nOldCursor )
Return Nil
Usando Array mostra que cada
ELEMENTO usa 1 byte de memória:
Código: Selecionar todos
#include "inkey.ch"
#include "hbmemory.ch"
Function Main()
local x, aString:={}, nOldCursor
AaDd( aString, Repl( "X", 1000024 ) ) // aString com 1 mega...
Clear Screen
nOldCursor := SetCursor( 0 )
For x:=1 To 1000024 // vamos tentar chegar a 1 tera...
AaDd( aString, aString )
? ""
? ""
? ""
? "Valores em KB:"
? ""
? "Free Variable Space...:", Memory( HB_MEM_CHAR )
? "Largest String........:", Memory( HB_MEM_BLOCK )
? "RUN Memory............:", Memory( HB_MEM_RUN )
? "Virtual Memory........:", Memory( HB_MEM_VM )
? ""
? "Em x sendo ...........:", x
? "Tamanho de aString....:", Len( aString )
If Inkey() = K_ESC
Exit
EndIf
SetPos( 0, 0 )
Next
SetCursor( nOldCursor )
Return Nil
Usar objetos
Enviado: 23 Jul 2023 09:48
por marcosgambeta
José,
A explicação está correta.
Usando seu exemplo mais recente como base, a instrução 'MemoRead( "openoffice41.exe" )' é executada UMA ÚNICA VEZ e o resultado é passado para a função AFILL. Cada elemento da array recebe o mesmo valor, de modo que todos usam o RESULTADO ÚNICO da função MEMOREAD.
Para obter o efeito que você quer, seria preciso executar a função MEMOREAD para cada item da array.
Algo assim:
Código: Selecionar todos
x[1] := MemoRead( "openoffice41.exe" )
x[2] := MemoRead( "openoffice41.exe" )
x[3] := MemoRead( "openoffice41.exe" )
x[4] := MemoRead( "openoffice41.exe" )
x[5] := MemoRead( "openoffice41.exe" )
...
Agora veja este teste:
Código: Selecionar todos
procedure main()
local s
s := space(2000000000) // 2.000.000.000
? len(s)
inkey(0)
return
Aqui dá este erro quando tento executar:
Código: Selecionar todos
Unrecoverable error 9006: hb_xgrab can't allocate memory Called from SPACE(0) Called from MAIN(5) in largestring2.prg
Meu Windows não pode reservar um bloco de memória deste tamanho (2.000.000.000), então retornou um ponteiro nullo para o Harbour.
O Harbour então, abortou o programa com o erro acima.
O Harbour usa a memória apenas se o sistema operacional der sinal verde, caso contrário dá erro.
Usar objetos
Enviado: 23 Jul 2023 10:29
por marcosgambeta
José,
Tem uma função na API do Windows que permite checar o uso da memória:
https://learn.microsoft.com/en-us/windo ... rystatusex
https://learn.microsoft.com/en-us/windo ... rystatusex
Segue abaixo a função implementada em C. Talvez ajude nos seus testes.
Código: Selecionar todos
procedure main()
local a
local s
a := MemoryStatus()
? "dwMemoryLoad...........:", a[1]
? "ullTotalPhys...........:", a[2]
? "ullAvailPhys...........:", a[3]
? "ullTotalPageFile.......:", a[4]
? "ullAvailPageFile.......:", a[5]
? "ullTotalVirtual........:", a[6]
? "ullAvailVirtual........:", a[7]
? "ullAvailExtendedVirtual:", a[8]
?
s := space(1000000000) // 1.000.000.000
a := MemoryStatus()
? "dwMemoryLoad...........:", a[1]
? "ullTotalPhys...........:", a[2]
? "ullAvailPhys...........:", a[3]
? "ullTotalPageFile.......:", a[4]
? "ullAvailPageFile.......:", a[5]
? "ullTotalVirtual........:", a[6]
? "ullAvailVirtual........:", a[7]
? "ullAvailExtendedVirtual:", a[8]
?
return
#pragma BEGINDUMP
#include <hbapi.h>
#include <windows.h>
HB_FUNC( MEMORYSTATUS )
{
MEMORYSTATUSEX ms;
memset(&ms, 0, sizeof(ms));
ms.dwLength = sizeof(ms);
GlobalMemoryStatusEx(&ms);
hb_reta(8);
hb_storvnl(ms.dwMemoryLoad, -1, 1);
hb_storvnll(ms.ullTotalPhys, -1, 2);
hb_storvnll(ms.ullAvailPhys, -1, 3);
hb_storvnll(ms.ullTotalPageFile, -1, 4);
hb_storvnll(ms.ullAvailPageFile, -1, 5);
hb_storvnll(ms.ullTotalVirtual, -1, 6);
hb_storvnll(ms.ullAvailVirtual, -1, 7);
hb_storvnll(ms.ullAvailExtendedVirtual, -1, 8);
}
#pragma ENDDUMP
Usar objetos
Enviado: 23 Jul 2023 11:43
por JoséQuintas
Responderam no harbour-devel.
Mas é um comportamento esquisito, e o exemplo é esquisito.
Hi JMCQuintas
I did a quick example (didn't fixed warnings) that illustrates how Harbour handles such strings:
Código: Selecionar todos
#define LEN 10
Procedure Main()
Local i
Local cText := "Hello World!"
Local aList := Array(LEN)
AFill(aList, cText)
For i := 1 To LEN
? i, aList[i], GetPointer(aList[i])
Next
?
// here we will change one string content:
hb_BPoke(@aList[1], 6, 64)
For i := 1 To LEN
? i, aList[i], GetPointer(aList[i])
Next
?
Return
#pragma BEGINDUMP
#include "hbapi.h"
HB_FUNC(GETPOINTER) {
hb_retptr(hb_parc(1));
}
#pragma ENDDUMP
Example output (hilighted important part)
1 Hello World! 0x00B400A8
2 Hello World! 0x00B400A8
3 Hello World! 0x00B400A8
4 Hello World! 0x00B400A8
5 Hello World! 0x00B400A8
6 Hello World! 0x00B400A8
7 Hello World! 0x00B400A8
8 Hello World! 0x00B400A8
9 Hello World! 0x00B400A8
10 Hello World! 0x00B400A8
1 Hello@World! 0x000901FC <--- note here
2 Hello World! 0x00B400A8
3 Hello World! 0x00B400A8
4 Hello World! 0x00B400A8
5 Hello World! 0x00B400A8
6 Hello World! 0x00B400A8
7 Hello World! 0x00B400A8
8 Hello World! 0x00B400A8
9 Hello World! 0x00B400A8
10 Hello World! 0x00B400A8
In the first loop, all items point to the same string on memory.
But I changed one string with BPoke, so Harbour "automagically"
makes a copy of the string and stores it elsewhere. All other 9
copies still mantain the original address.
(thanks devs for the proper/smart copy-on-change)
Also sorry if there's already a built-in to check some variable's
address, I don't remember needing such a thing and I'm a little
busy to check, so I create that "kludgy" C code.
Hope it helps to understand
Se o comportamento esquisito for correto, o que acontecerá com objetos?
Usar objetos
Enviado: 23 Jul 2023 12:11
por JoséQuintas
Agora entendi o exemplo.
Teria sido mais prático dizer que INTERNAMENTE o harbour usa uma única string, mas que pode se multiplicar conforme for alterada.
Agora também entendi o hb_bpoke()
Achei interessante.
Ao invés de substr(x,1,10) + letra + Substr(x,12), troca direto o caractere da posição.
Pode agilizar muitas rotinas com string grande.
Talvez Stuff() faça a mesma coisa, não sei se com a mesma velocidade.
Pra quem não entendeu fonte, assim como não entendi a primeira vez:
O fonte mostrou que todas as strings eram a mesma, indicando o mesmo "pointer", o mesmo endereço de memória.
Ao alterar uma string, o "pointer" passou a ser outro, outro endereço de memória.
O Harbour acabou usando uma única cópia daqueles 200MB pra 1 milhão de arrays.
Apesar da soma dar terabytes, na memória eram só 200MB.
Mas se eu tentar alterar uma única letra em cada, vai estourar a memória antes de terminar, porque o Harbour vai criando cópia diferente pra cada elemento, o que passa a realmente usar 200MB por elemento alterado.
É como se fosse passar por referência, mas só internamente pra uso do Harbour, não fica visível no aplicativo, a não ser usando aquelas funções do exemplo. E internamente ele vai desvinculando isso, conforme precisa, o aplicativo continua não sabendo que isso acontece, e nem precisa saber.