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.