Cheb's Game Engine

Планы, идеология, архитектура и т.п.

Модератор: Модераторы

Re: Cheb's Game Engine

Сообщение Cheb » 26.02.2017 19:07:30

Пробовал подцепить heaptrc. В зависимости от набора ключей компиляции, оная либо падает из-за моей lineinfo, либо моя lineinfo не работает и heaptrc выдаёт все доклады в шестнадцатеричных адресах, что бесполезно чуть менее, чем полностью.
А в модуле у неё вообще бомбит - ибо чеперси так устроена, что не стесняясь пишет за концом выделенного блока, из здравого рассуждения, что выделенный блок будет выровнен уж на 4 байта как минимум.

Вывод: придётся запиливать свою heaptrc с блекджеком и шлюхами.
Вот радость-то. :evil:
Последний раз редактировалось Cheb 26.02.2017 21:29:10, всего редактировалось 1 раз.
Аватара пользователя
Cheb
энтузиаст
 
Сообщения: 994
Зарегистрирован: 06.06.2005 15:54:34

Re: Cheb's Game Engine

Сообщение runewalsh » 26.02.2017 20:25:18

Проще в heaptrc отключить/подправить проверку повреждения памяти (если дело в ней)...
Аватара пользователя
runewalsh
энтузиаст
 
Сообщения: 578
Зарегистрирован: 27.04.2010 00:15:25

Re: Cheb's Game Engine

Сообщение vitaly_l » 26.02.2017 20:33:28

Cheb писал(а):запиливать свою heaptrc

В поиск этого сайта вбей heaptrcex и нажми enter, возможно подойдёт.
Аватара пользователя
vitaly_l
долгожитель
 
Сообщения: 3333
Зарегистрирован: 31.01.2012 16:41:41

Re: Cheb's Game Engine

Сообщение Cheb » 27.02.2017 02:40:33

вбей heaptrcex и нажми enter,

Почитал.

Ааа, проще самому сделать...
К тому же, если они тянут за собой lineinfo - это тянет за собой неработоспособность chelinfo.

...

Итак, что я сегодня выучил на своей шкуре?

НЕВОЗМОЖНО построить свой диспетчер памяти как надстройку над системным (расширяя размер блока доп. полями), поскольку системный... (фанфары)... обращается к текущему диспетчеру памяти через методы переменной MemoryManager, в которую кастомный менеджер памяти как раз устанавливает свои функции.

Руки бы отрывать этим людям.

Потом соберусь, сочиню багрепорт на эту тему. Бардак, ё.
Аватара пользователя
Cheb
энтузиаст
 
Сообщения: 994
Зарегистрирован: 06.06.2005 15:54:34

Re: Cheb's Game Engine

Сообщение runewalsh » 27.02.2017 05:10:24

В самом деле, похоже на баг. Не соглашусь насчёт НЕВОЗМОЖНОСТИ, обойти легко:
Код: Выделить всё
{$mode objfpc} {$typedaddress on}
{-$define NoteParasiteAllocations}

var
   OldMgr: TMemoryManager;
   NextBlockNumber: cardinal {= 0};
   InsideCall: cardinal {= 0}; // <--- this

type
   pBlockExtra = ^BlockExtra;
   BlockExtra = object
      blockNumber: cardinal;
      queriedSize: SizeUint;
      function Size: SizeUint; static;
   const
      Alignment = 2 * sizeof(pointer);
   end;

   function BlockExtra.Size: SizeUint;
   begin
      result := align(sizeof(BlockExtra), Alignment);
   end;

   function GetMemImpl(size: PtrUint): pointer;
   begin
      if InsideCall > 0 then
      begin
      {$ifdef NoteParasiteAllocations} writeln('(allocating ', size, ' bytes block)'); {$endif}
         exit(OldMgr.GetMem(size));
      end;
      writeln('allocating ', size, ' bytes for block #', NextBlockNumber);
      result := OldMgr.GetMem(BlockExtra.Size + size) + BlockExtra.Size;
      pBlockExtra(result - BlockExtra.Size)^.blockNumber := NextBlockNumber;
      pBlockExtra(result - BlockExtra.Size)^.queriedSize := size;
      inc(NextBlockNumber);
   end;

   function FreeMemImpl(p: pointer): PtrUint;
   begin
      if InsideCall > 0 then
      begin
      {$ifdef NoteParasiteAllocations} writeln('(freeing old block #', pBlockExtra(p)^.blockNumber, ')'); {$endif}
         exit(OldMgr.FreeMem(p));
      end;
      writeln('freeing block #', pBlockExtra(p - BlockExtra.Size)^.blockNumber, ' (', pBlockExtra(p - BlockExtra.Size)^.queriedSize, ' bytes)');
      OldMgr.FreeMem(p - BlockExtra.Size);
      result := 0;
   end;

   function FreeMemSizeImpl(p: pointer; size: PtrUint): PtrUint;
   begin
      Assert(size = size);
      result := FreeMemImpl(p);
   end;

   function AllocMemImpl(size: PtrUint): pointer;
   begin
      result := GetMemImpl(size);
      fillchar(result^, size, 0);
   end;

   function ReallocMemImpl(var p: pointer; size: PtrUint): pointer;
   begin
      if size = 0 then
      begin
         FreeMemImpl(p);
         result := nil;
      end else
         if not Assigned(p) then
            result := GetMemImpl(size)
         else
         begin
            writeln('reallocating block #', pBlockExtra(p - BlockExtra.Size)^.blockNumber, ' from ', pBlockExtra(p - BlockExtra.Size)^.queriedSize, ' to ', size, ' bytes');
            inc(InsideCall);
            try
               p := p - BlockExtra.Size;
               result := OldMgr.ReallocMem(p, BlockExtra.Size + size) + BlockExtra.Size;
               pBlockExtra(result - BlockExtra.Size)^.queriedSize := size;
            finally
               dec(InsideCall);
            end;
         end;
      p := result;
   end;

   function MemSizeImpl(p: pointer): PtrUint;
   begin
      result := OldMgr.MemSize(p - BlockExtra.Size) - BlockExtra.Size;
   end;

var
   NewMgr: TMemoryManager;
   a, b, c: pointer;

begin
   GetMemoryManager((@OldMgr)^);
   NewMgr := OldMgr;
   NewMgr.NeedLock    := true;
   NewMgr.GetMem      := @GetMemImpl;
   NewMgr.FreeMem     := @FreeMemImpl;
   NewMgr.FreeMemSize := @FreeMemSizeImpl;
   NewMgr.AllocMem    := @AllocMemImpl;
   NewMgr.ReallocMem  := @ReallocMemImpl;
   NewMgr.MemSize     := @MemSizeImpl;
   SetMemoryManager(NewMgr);

   writeln('> a := GetMem(10);');      a := GetMem(10);
   writeln(#10'> b := GetMem(20);');   b := GetMem(20);
   writeln(#10'> ReallocMem(a, 30);'); ReallocMem(a, 30);
   writeln(#10'> c := GetMem(40);');   c := GetMem(40);
   writeln(#10'> ReallocMem(b, 50);'); ReallocMem(b, 50);
   writeln(#10'> ReallocMem(c, 60);'); ReallocMem(c, 60);
   writeln(#10'> ReallocMem(b, 70);'); ReallocMem(b, 70);
   writeln(#10'> FreeMem(a);');        FreeMem(a);
   writeln(#10'> FreeMem(b);');        FreeMem(b);
   writeln(#10'> FreeMem(c);');        FreeMem(c);
   readln;

   SetMemoryManager(OldMgr);
end.

Код: Выделить всё
> a := GetMem(10);
allocating 10 bytes for block #0

> b := GetMem(20);
allocating 20 bytes for block #1

> ReallocMem(a, 30);
reallocating block #0 from 10 to 30 bytes

> c := GetMem(40);
allocating 40 bytes for block #2

> ReallocMem(b, 50);
reallocating block #1 from 20 to 50 bytes

> ReallocMem(c, 60);
reallocating block #2 from 40 to 60 bytes

> ReallocMem(b, 70);
reallocating block #1 from 50 to 70 bytes

> FreeMem(a);
freeing block #0 (30 bytes)

> FreeMem(b);
freeing block #1 (70 bytes)

> FreeMem(c);
freeing block #2 (60 bytes)

В предположении, что NeedLock блокирует все вызовы, можно не делать InsideCall threadvar-ом
Аватара пользователя
runewalsh
энтузиаст
 
Сообщения: 578
Зарегистрирован: 27.04.2010 00:15:25

Re: Cheb's Game Engine

Сообщение Cheb » 27.02.2017 11:50:55

А) Я не могу, к сожалению, использовать treadvar'ы (слишком упоротая архитектура, есть подозрение, что их развезёт)
Б) Я уже планировал делать собственный диспетчер памяти с блекджеком и шлюхами (поддержка дополнительных полей, хранимых отдельно от самого блока памяти, переключение диспетчера памяти на лету, блокировка памяти, выделенной конкретным диспетчером в конкретном потоке, от записи посредством VirtualProtect).

Ну, реализую это раньше, чем планировал. Делов-то.
Зато утечкоотслеживательный станет лишь ещё одним винтиком в этом механизме.

Добавлено спустя 13 минут 2 секунды:
Вкратце о планируемом:
Все мои диспетчеры памяти устроены по одному шаблону, оперируя 4-килобайтными виртуальными страницами, получаемыми напрямую от системы. Простым обрезанием указателя по маске получаешь ссылку на служебной информации, которая, в том числе, содержит ссылку на диспетчер памяти, заведующий этой страницей и необязательную ссылку на дополнительный блок (выделяется базовым диспетчером памяти по запросу специализированного, лежит в другом месте).
В каждой странице располагаются блоки строго одинакового размера, на каждый размер блока - как минимум по странице (размеры округляются до линейки кеша: 64, 128, 192 и т.д.)
На блоки больше примерно 1900 байт выделяется целое число страниц, запрашиваемых напрямую от ОС (VirtualAlloc в винде). Служебный блок содержит счётчики предсказателей роста, чтобы при монотонном росте захапывать с запасом. В том числе за счёт MEM_RESERVE (застолблённая, но физически не выделенная виртуальная память).

Добавлено спустя 2 часа 31 минуту 11 секунд:
З.Ы. Насколько я разобрался в штатном диспетчере:

1. NeedLock - это устаревшая затычка, ни на что не влияет
2. Насколько я успел понять - там так хитро устроено, что у каждого потока - свой пул, и когда освобождает "не родной" блок - то он не освобождается, а ставится в специальную очередь, и тот поток, чей этот блок, при следующей операции заодно освобождает все блоки в очереди. Причём, в критическую секцию завёрнуты именно операции с этой очередью, поэтому при штатной работе (с родными для потока блоками) никаких критических секций не используется.

Добавлено спустя 34 минуты 52 секунды:
З.З.Ы. Мой хитрый план: запрячь механизмы страниц виртуальной памяти чтобы НЕ копировать ничего при росте выделенного куска (пример: тупо строка набирается по буковке, и так - мегабайт на сто), а отображать те же страницы памяти на другой кусок адресного пространства если дальше расти некуда.
Волшебные слова: mremap (Linux 2.4 и выше), CreateFileMapping (Windows XP и выше)

Добавлено спустя 3 часа 50 минут 8 секунд:
З.З.З.Ы. Я так, кстати, могу наконец избавиться от геморроя и передавать между екзешником и модулем нормальные строки, без плясок с бубном и приведения к PChar и обратно. Просто сделать сопряжение диспетчеров памяти, чтобы опознав "чужой" блок они переадресовали его через специальный интерфейс к нужному диспетчеру того исполняемого модуля.
Аватара пользователя
Cheb
энтузиаст
 
Сообщения: 994
Зарегистрирован: 06.06.2005 15:54:34

Re: Cheb's Game Engine

Сообщение скалогрыз » 27.02.2017 21:16:15

Деструктивная критика: ты как-то очень далеко от написания непосредственно игрового движка :?

а нельзя как-то отделить игровой движок, от менеджеров памяти (... движка памяти)?
скалогрыз
долгожитель
 
Сообщения: 1803
Зарегистрирован: 03.09.2008 02:36:48

Re: Cheb's Game Engine

Сообщение Cheb » 27.02.2017 21:40:51

Можно, и даже начать делать интересные вещи, но пока память течёт мегабайт в секунду на пустом месте - мне придётся его перезапускать каждые минут двадцать, чтобы не падало.

Ничего не деструктивная, всё правильно.
Меня уже задрало полировать какашку :evil: Но ничего не поделаешь, без специализированного диспетчера памяти я нормально мультиплеерную модель не реализую, а прикручиваеть её задним числом может стать себе дороже. Например, если я создам тулзет и на этом тулзете что-то сделаю - я резко потеряю возможность перетряхивать формат и структуру БД.
Так что, лучше уж, кое-какие фичи заложить в фундамент. Или, хотя бы, затычки под них.

Текущий план - форматы 3д мешей: для редактирования / модификации и финализированный для рендера (зело разные требования к ним) + текстурный атлас и мультитекстурирование мешей, которые потом будут чанками ландшафта.
+ попутно дорезать таки остающиеся glBegin..glEnd чтобы взлетело на GL ES 2 Raspberry Pi 2, а не падало мордой об вызов по нулевому адресу, как сейчас.
Аватара пользователя
Cheb
энтузиаст
 
Сообщения: 994
Зарегистрирован: 06.06.2005 15:54:34

Re: Cheb's Game Engine

Сообщение Cheb » 11.03.2017 14:15:34

Сейчас на работе завал, чентра стоит, но пока что накопал интересные факты:

В винде нет mremap, придётся это эмулировать используя отображение файла. Проблема была что надо сразу зарезервировать максимальный кусок пространства в файле подкачки, отчего у того вполне рожа треснет. А на лету увеличивать невозможно. То есть, ты этой аллокацией сразу себе создаёшь потолок.

Но в винде (какое совпадение: как раз начиная с XP, которая у меня тоже минимум по требованиям) можно создавать разрежённый файл подкачки своими руками. См. https://dzone.com/articles/sparse-and-m ... pped-files
Конечно, будет *некоторый* геморрой с тем, что подцеплять этот буфер придётся на лету, много позже запуска диспетчера памяти (надо прочитать конфиги, пропарсить файловую систему на предмет NTFS диска с достаточным свободным пространством и т.п.) но оно того стоит.

Какой, спросите, профит?..
Код: Выделить всё
var s: AnsiString; i: integer;
begin
  for i:=0 to 100000000 do s:= s + 'a';

станет зверски эффективным кодом :mrgreen:
Аватара пользователя
Cheb
энтузиаст
 
Сообщения: 994
Зарегистрирован: 06.06.2005 15:54:34

Re: Cheb's Game Engine

Сообщение runewalsh » 11.03.2017 18:35:59

Да вот не очень. Перефразируя кого-то с гд.ру, 0 раз вызвать Realloc быстрее, чем 1000000 раз вызвать MyUltraFastRealloc.
Код: Выделить всё
{$mode objfpc} {$h+} {$typedaddress on} {$coperators on}
uses
   Windows;

   function QPC: double;
   var
      counter, frequency: TLargeInteger;
   begin
      QueryPerformanceFrequency((@frequency)^);
      QueryPerformanceCounter((@counter)^);
      result := counter / frequency;
   end;

   procedure TestConcat(var s: ansistring; iterations: integer);
   var
      it: integer;
   begin
      for it := 1 to iterations do
         s += 'a';
   end;

   procedure TestSmartConcat(var s: ansistring; iterations: integer);
   var
      pos: SizeInt;
   begin
      pos := length(s) + 1;
      SetLength(s, length(s) + length('a') * iterations);

      while pos + length('a') - 1 <= length(s) do
      begin
         s[pos] := 'a';
         pos += length('a');
      end;
   end;

const
   Iterations = 15000000;
   HugeBlock = 2 * Iterations * sizeof(char);

var
   OldMgr, NewMgr: TMemoryManager;

   function FastGetMem(size: SizeUint): pointer;
   begin
      Assert(size <= HugeBlock);
      result := OldMgr.GetMem(HugeBlock);
   end;

   function FastReallocMem(var p: pointer; size: SizeUint): pointer;
   begin
      Assert(size <= HugeBlock);
      Assert(Assigned(p) and (size > 0));
      result := p;
   end;

var
   s: string = '';
   timeC, timeCMM, timeSC: double;

begin
   timeC := QPC;
   TestConcat(s, Iterations);
   timeC := QPC - timeC;
   writeln('Concat w/default manager (', length(s), '): ', timeC:0:2, ' s (', timeC/iterations*1e9:0:2, ' ns/sym)');
   s := '';

   GetMemoryManager((@OldMgr)^);
   NewMgr := OldMgr;
   NewMgr.GetMem := @FastGetMem;
   NewMgr.ReallocMem := @FastReallocMem;
   SetMemoryManager(NewMgr);

   timeCMM := QPC;
   TestConcat(s, Iterations);
   timeCMM := QPC - timeCMM;
   writeln('Concat w/fast manager (', length(s), '): ', timeCMM:0:2, ' s (', timeCMM/iterations*1e9:0:2, ' ns/sym)');
   s := '';

   SetMemoryManager(OldMgr);

   timeSC := QPC;
   TestSmartConcat(s, Iterations);
   timeSC := QPC - timeSC;
   writeln('SmartConcat w/default manager (', length(s), '): ', timeSC:0:2, ' s (', timeSC/iterations*1e9:0:2, ' ns/sym)');
   s := '';

   readln;
end.

Код: Выделить всё
Concat w/default manager (15000000): 2.01 s (133.72 ns/sym)
Concat w/fast manager (15000000): 1.00 s (66.54 ns/sym)
SmartConcat w/default manager (15000000): 0.07 s (4.46 ns/sym)

С самым быстрым менеджером всего вдвое быстрее, зато с одной аллокацией — в 30 раз. И это ещё s[index] := symbol дёргает UniqueString, PChar(s)[index - 1] := symbol уменьшит время выполнения ещё вдвое. Менеджер здесь — последняя проблема, короче.
Аватара пользователя
runewalsh
энтузиаст
 
Сообщения: 578
Зарегистрирован: 27.04.2010 00:15:25

Re: Cheb's Game Engine

Сообщение Cheb » 11.03.2017 21:55:16

Ну, во первых, не 2, а 3 раза:
Код: Выделить всё
Concat w/default manager (15000000): 1.21 s (80.51 ns/sym)
Concat w/fast manager (15000000): 0.37 s (24.60 ns/sym)
SmartConcat w/default manager (15000000): 0.07 s (4.40 ns/sym)


Во вторых, на практике, я таки буду использовать динамические массивы из упакованных записей по 64 байта (структура чанков мира).
Если тест слегка модифицировать, то оказывается, что на блоках 64 байта быстрофейковый ММ в 80 раз быстрее стокового и всего лишь вдвое медленнее умной реаллокации.

А размеры мне заранее известны редко будут.

Код: Выделить всё
program testrealloc;

{$mode objfpc} {$h+} {$typedaddress on} {$coperators on}
uses
   Windows;

   var aaa: AnsiString;
   
   function QPC: double;
   var
      counter, frequency: TLargeInteger;
   begin
      QueryPerformanceFrequency((@frequency)^);
      QueryPerformanceCounter((@counter)^);
      result := counter / frequency;
   end;

   procedure TestConcat(var s: ansistring; iterations: integer);
   var
      it: integer;
   begin
      for it := 1 to iterations do
         s += aaa;
   end;

   procedure TestSmartConcat(var s: ansistring; iterations: integer);
   var
      pos: SizeInt;
   begin
      pos := length(s) + 1;
      SetLength(s, length(s) + length(aaa) * iterations);

      while pos + length(aaa) - 1 <= length(s) do
      begin
         MOVE(aaa[1], s[pos], sizeof(aaa[1]) * length(aaa));
         pos += length(aaa);
      end;
   end;

const
   blcksize = 15000000;
   HugeBlock = 2 * blcksize * sizeof(char);

var
   OldMgr, NewMgr: TMemoryManager;

   function FastGetMem(size: SizeUint): pointer;
   begin
      Assert(size <= HugeBlock);
      result := OldMgr.GetMem(HugeBlock);
   end;

   function FastReallocMem(var p: pointer; size: SizeUint): pointer;
   begin
      Assert(size <= HugeBlock);
      Assert(Assigned(p) and (size > 0));
      result := p;
   end;

var
   s: string = '';
   timeC, timeCMM, timeSC: double;
   i, j, k: integer;
   Iterations: integer;
begin
   for i:= 0 to 8 do begin
    j:= 1 shl i;
    writeln(j, ' bytes:');
    aaa:= '';
    for k:= 1 to j do aaa+= 'a';
    Iterations:= blcksize div length(aaa);
       
     
     timeC := QPC;
     TestConcat(s, Iterations);
     timeC := QPC - timeC;
     writeln('Concat w/default manager (', length(s), '): ', timeC:0:2, ' s (', timeC/iterations*1e9:0:2, ' ns/sym)');
     s := '';

     GetMemoryManager((@OldMgr)^);
     NewMgr := OldMgr;
     NewMgr.GetMem := @FastGetMem;
     NewMgr.ReallocMem := @FastReallocMem;
     SetMemoryManager(NewMgr);

     timeCMM := QPC;
     TestConcat(s, Iterations);
     timeCMM := QPC - timeCMM;
     writeln('Concat w/fast manager (', length(s), '): ', timeCMM:0:2, ' s (', timeCMM/iterations*1e9:0:2, ' ns/sym)');
     writeln('Ratio: ', timeC / timeCMM:0:2);
     
     s := '';

     SetMemoryManager(OldMgr);

     timeSC := QPC;
     TestSmartConcat(s, Iterations);
     timeSC := QPC - timeSC;
     writeln('SmartConcat w/default manager (', length(s), '): ', timeSC:0:2, ' s (', timeSC/iterations*1e9:0:2, ' ns/sym)');
     writeln('Ratio: ',timeCMM / timeSC:0:2);
     
     s := '';
     writeln;
   end;

   readln;
end.


(нижнее Ratio это fast MM / smart concat)
Код: Выделить всё
1 bytes:
Concat w/default manager (15000000): 1.12 s (74.85 ns/sym)
Concat w/fast manager (15000000): 0.40 s (26.35 ns/sym)
Ratio: 2.84
SmartConcat w/default manager (15000000): 0.11 s (7.66 ns/sym)
Ratio: 3.44

2 bytes:
Concat w/default manager (15000000): 0.98 s (131.07 ns/sym)
Concat w/fast manager (15000000): 0.20 s (26.73 ns/sym)
Ratio: 4.90
SmartConcat w/default manager (15000000): 0.06 s (8.43 ns/sym)
Ratio: 3.17

4 bytes:
Concat w/default manager (15000000): 0.90 s (239.76 ns/sym)
Concat w/fast manager (15000000): 0.12 s (31.06 ns/sym)
Ratio: 7.72
SmartConcat w/default manager (15000000): 0.04 s (10.54 ns/sym)
Ratio: 2.95

8 bytes:
Concat w/default manager (15000000): 0.86 s (459.32 ns/sym)
Concat w/fast manager (15000000): 0.05 s (28.12 ns/sym)
Ratio: 16.33
SmartConcat w/default manager (15000000): 0.02 s (9.45 ns/sym)
Ratio: 2.98

16 bytes:
Concat w/default manager (15000000): 0.84 s (894.72 ns/sym)
Concat w/fast manager (15000000): 0.03 s (31.40 ns/sym)
Ratio: 28.50
SmartConcat w/default manager (15000000): 0.01 s (12.71 ns/sym)
Ratio: 2.47

32 bytes:
Concat w/default manager (15000000): 0.84 s (1795.98 ns/sym)
Concat w/fast manager (15000000): 0.02 s (36.75 ns/sym)
Ratio: 48.87
SmartConcat w/default manager (15000000): 0.01 s (17.75 ns/sym)
Ratio: 2.07

64 bytes:
Concat w/default manager (15000000): 0.83 s (3553.46 ns/sym)
Concat w/fast manager (15000000): 0.01 s (43.75 ns/sym)
Ratio: 81.22
SmartConcat w/default manager (15000000): 0.01 s (28.49 ns/sym)
Ratio: 1.54

128 bytes:
Concat w/default manager (14999936): 0.83 s (7062.42 ns/sym)
Concat w/fast manager (14999936): 0.01 s (67.95 ns/sym)
Ratio: 103.93
SmartConcat w/default manager (14999936): 0.01 s (51.94 ns/sym)
Ratio: 1.31

256 bytes:
Concat w/default manager (14999808): 0.82 s (14055.24 ns/sym)
Concat w/fast manager (14999808): 0.01 s (111.87 ns/sym)
Ratio: 125.64
SmartConcat w/default manager (14999808): 0.01 s (105.47 ns/sym)
Ratio: 1.06


Кстати, в комплекте с фпц идёт pagemem.pp, выделяющая по страницам работающая по сходным принципам.

Добавлено спустя 1 час 45 минут 31 секунду:
З.Ы. Увеличиваем размер с 15 до 100 мегабайт:
Код: Выделить всё
1 bytes:
Concat w/default manager (100000000): 37.92 s (379.22 ns/sym)
Concat w/fast manager (100000000): 2.61 s (26.13 ns/sym)
Ratio: 14.51
SmartConcat w/default manager (100000000): 0.76 s (7.56 ns/sym)
Ratio: 3.45

2 bytes:
Concat w/default manager (100000000): 35.84 s (716.82 ns/sym)
Concat w/fast manager (100000000): 1.33 s (26.55 ns/sym)
Ratio: 27.00
SmartConcat w/default manager (100000000): 0.42 s (8.43 ns/sym)
Ratio: 3.15

4 bytes:
Concat w/default manager (100000000): 35.63 s (1425.38 ns/sym)
Concat w/fast manager (100000000): 0.74 s (29.52 ns/sym)
Ratio: 48.29
SmartConcat w/default manager (100000000): 0.26 s (10.55 ns/sym)
Ratio: 2.80

8 bytes:
Concat w/default manager (100000000): 35.68 s (2854.53 ns/sym)
Concat w/fast manager (100000000): 0.35 s (27.66 ns/sym)
Ratio: 103.21
SmartConcat w/default manager (100000000): 0.12 s (9.28 ns/sym)
Ratio: 2.98

16 bytes:
Concat w/default manager (100000000): 35.90 s (5743.79 ns/sym)
Concat w/fast manager (100000000): 0.20 s (31.55 ns/sym)
Ratio: 182.07
SmartConcat w/default manager (100000000): 0.08 s (12.13 ns/sym)
Ratio: 2.60

32 bytes:
Concat w/default manager (100000000): 36.02 s (11524.86 ns/sym)
Concat w/fast manager (100000000): 0.11 s (35.61 ns/sym)
Ratio: 323.64
SmartConcat w/default manager (100000000): 0.05 s (16.87 ns/sym)
Ratio: 2.11

64 bytes:
Concat w/default manager (100000000): 36.03 s (23058.25 ns/sym)
Concat w/fast manager (100000000): 0.07 s (42.33 ns/sym)
Ratio: 544.71
SmartConcat w/default manager (100000000): 0.04 s (27.29 ns/sym)
Ratio: 1.55

128 bytes:
Concat w/default manager (100000000): 36.20 s (46335.83 ns/sym)
Concat w/fast manager (100000000): 0.05 s (59.49 ns/sym)
Ratio: 778.90
SmartConcat w/default manager (100000000): 0.04 s (51.64 ns/sym)
Ratio: 1.15

256 bytes:
Concat w/default manager (100000000): 35.99 s (92136.14 ns/sym)
Concat w/fast manager (100000000): 0.04 s (110.75 ns/sym)
Ratio: 831.96
SmartConcat w/default manager (100000000): 0.04 s (101.05 ns/sym)
Ratio: 1.10


Добавлено спустя 2 часа 22 минуты 29 секунд:
Тест на гигабайт гонится. Ножка полки с ноутбуком (растёт вниз от полки с цветами и принтером) уже горячая, на всю свою четырёхсантиметровую толщину. Потму что выхлоп аккурат на неё попадает.

Код: Выделить всё
1 bytes:
Concat w/default manager (1000000000): 3999.36 s (3999.36 ns/sym)
Concat w/fast manager (1000000000): 26.81 s (26.81 ns/sym)
Ratio: 149.16
SmartConcat w/default manager (1000000000): 7.69 s (7.69 ns/sym)
Ratio: 3.49

2 bytes:
Concat w/default manager (1000000000): 3855.59 s (7711.18 ns/sym)
Concat w/fast manager (1000000000): 13.93 s (27.85 ns/sym)
Ratio: 276.87
SmartConcat w/default manager (1000000000): 4.34 s (8.68 ns/sym)
Ratio: 3.21

4 bytes:

- иии здравствуй, гомерическая прогрессия. Уже имеем час с хвостиком против 14 секунд. :mrgreen:

Добавлено спустя 1 час 7 минут 46 секунд:
Код: Выделить всё
Concat w/default manager (1000000000): 4319.92 s (17279.70 ns/sym)
Concat w/fast manager (1000000000): 7.45 s (29.81 ns/sym)
Ratio: 579.57
SmartConcat w/default manager (1000000000): 2.51 s (10.06 ns/sym)
Ratio: 2.96

8 bytes:


Добавлено спустя 7 часов 54 минуты 26 секунд:
Код: Выделить всё
Concat w/default manager (1000000000): 3731.54 s (29852.35 ns/sym)
Concat w/fast manager (1000000000): 3.40 s (27.19 ns/sym)
Ratio: 1097.91
SmartConcat w/default manager (1000000000): 1.17 s (9.37 ns/sym)
Ratio: 2.90

16 bytes:
Concat w/default manager (1000000000): 3680.53 s (58888.54 ns/sym)
Concat w/fast manager (1000000000): 1.93 s (30.92 ns/sym)
Ratio: 1904.44
SmartConcat w/default manager (1000000000): 0.74 s (11.85 ns/sym)
Ratio: 2.61

32 bytes:
Concat w/default manager (1000000000): 3692.76 s (118168.29 ns/sym)
Concat w/fast manager (1000000000): 1.11 s (35.61 ns/sym)
Ratio: 3318.32
SmartConcat w/default manager (1000000000): 0.53 s (17.04 ns/sym)
Ratio: 2.09

64 bytes:
Concat w/default manager (1000000000): 3717.94 s (237948.02 ns/sym)
Concat w/fast manager (1000000000): 0.68 s (43.45 ns/sym)
Ratio: 5476.52
SmartConcat w/default manager (1000000000): 0.42 s (26.97 ns/sym)
Ratio: 1.61

128 bytes:
Concat w/default manager (1000000000): 3710.61 s (474957.93 ns/sym)
Concat w/fast manager (1000000000): 0.48 s (60.91 ns/sym)
Ratio: 7797.62
SmartConcat w/default manager (1000000000): 0.40 s (51.32 ns/sym)
Ratio: 1.19

256 bytes:
Concat w/default manager (1000000000): 3754.27 s (961093.90 ns/sym)
Concat w/fast manager (1000000000): 0.44 s (112.52 ns/sym)
Ratio: 8541.82
SmartConcat w/default manager (1000000000): 0.40 s (101.52 ns/sym)
Ratio: 1.11



Добавлено спустя 9 часов 18 минут 9 секунд:
Самое важное: я смогу обходу дерева объектов (и исохранению, которое тоже осуществляется обходом) добавить маску по ThreadID'ам, с пренебрежимо малым пенальти на производительность, и тем самым сделать чеперси многопоточной. Например, задействовать *все* ядра 8-ядерника для просчёта лагокомпенсации в мультиплеере. Или задействовать *все* ядра для ИИ в сингл-плеере.

Хмм... Есля я буду результаты решений ИИ пропускать через те же ворота, что и действия игроков мышью, и упаковывать их достаточно компактно, я смогу и в мультиплеере задействовать полный ИИ, только у сервера лишния ядра будут думать за неписей, а на клиентах неписи будут болванчиками, управляемыми командами с сервера, а лишние ядра заняты ускоренной промоткой параллельных ветвей реальности.
Причём, команды ИИ должны быть не непрерывным потоком "шевелил мышью влево, жал вперёд" как у игроков. От этого сеть треснет. Нет, они должны быть более абстрактными типа "пойти к навигационной точке Ы и там осмотреться" или "жрать повер-апы" или "обитеться на игрока Н". Т.е. в пределах одного решения в две-четыре секунды. А все рефлексы, что в районе 10 тиков в секунду - таки локальные. Но тупыыыые.
Аватара пользователя
Cheb
энтузиаст
 
Сообщения: 994
Зарегистрирован: 06.06.2005 15:54:34

Re: Cheb's Game Engine

Сообщение Cheb » 09.04.2017 12:24:22

Это была былинная попаболь, но я таки завершил переделывание всей кухни lineinfo с dwarf2 на stabs.
Поому что, как бы компактна ни была dwarf2, паскаль её явно поддерживает постольку поскольку, и на платформах, отличных от Intel-32, она генерируется через анус. А именно, покрытие диапазона адресов получается дырявое, как ковёр, поеденный молью. Особенно у модулей, которые состоят из 100500 инклюдов. :x

Для справки, парсер dwarf2 я заимствовал из фпц 2.4 и ниже (где она была штатной) и очень серьёзно доводил напильником (ибо без страшных хаков просто не работает). После моих багрепортов, похоже, разрабы поняли, что эту... массу лучше удавить, и парсер dwarf2 из RTL по тихому выкинули. А штатная lineinfo (-gl) использует, как раз, stabs.

А у меня острая аллергия на большие екзешники :evil: , а усушке и утруске их подвергать нельзя, чтобы lineinfo не отключилась - оттуда и вся пляска с бубнами вокруг выселения отладочной информации во внешние файлы, до кучи пожатые TCompressionStream.
И вот после всего вышеописанного получается 350-килобайтный .exe, к которому прилагается 560-килобайтный .zstabs (625 / 380 К для ARM на Raspberry Pi 2)
Отладочную инфу я потом замету под ковёр (чтобы лежала с глаз долой в отдельной папочке) и останутся дистиллированные понты самых маленьких екзешников 21-го века 8)

Ещё одна плюшка - в отличие от dwarf, stabs умеет названия функций :D

Добавлено спустя 6 часов 33 минуты 20 секунд:
Полез собирать DLL игрового модуля под линуксами. Был шокирован, осознав, что нигде нет типового .sh на эту тему.
Полез в архивы - нету нигде, мать его! Даже за 15-й год. Даже за 13-й.
Дунул, плюнул, слабал.
Собралось.
Внезапно! оказалось, что екзешник не умеет подбирать DLL по имени под платформу! :shock:
Допилил.
Модуль загрузился, резво инициализировался... И упал на ровном месте, вылезло исключение при попытке распарсить бектрейс исключения.

И вот я теперь думаю, а в каком году я последний раз под линуксом DLL'ы проверял?
Закрадывается абсурдное ощущение, что ещё при Ельцине :x
Аватара пользователя
Cheb
энтузиаст
 
Сообщения: 994
Зарегистрирован: 06.06.2005 15:54:34

Re: Cheb's Game Engine

Сообщение Cheb » 12.04.2017 03:18:06

Нежданчик: viewtopic.php?f=13&t=22612 :mrgreen:

А также, с интересом обнаружил, что под линуксами typeinfo(Utf8String) не равно typeinfo(AnsiString)
Или чё-то типа того. По крайней мере, регистрация классов Чеперси на этом встала.
Разбираться буду когда будет время.

З.Ы. Эпично:
Код: Выделить всё
{$ifdef che_unicode}
  {$if FPC_FULLVERSION<30000}
    {$fatal Impossible to use this compiler version: RTL is not unicode} //The sad truth
  {$endif}

  { modeswitch unicodestrings} //is in the main project file

  { warn IMPLICIT_STRING_CAST Error}
  {$warn IMPLICIT_STRING_CAST_LOSS Error}
  { warn EXPLICIT_STRING_CAST Error}
  {$warn EXPLICIT_STRING_CAST_LOSS Error}
  { if FPC_FULLVERSION<=40000} //***TODO Replace with version in which it is fixed
     {$define fix_fpc3_unicode} // Use custom mods of TFileStream, TStringList and TIniFile
  { endif}

  AnsiString1251 = type AnsiString(1251);
  TFileNameString = UnicodeString;
  PFileNameChar = PUnicodeChar;

{$else}
  //Legacy Free Pascal below 3.0 (2.6.0 to 2.6.4)
  RawByteString = AnsiString;
  AnsiString1251 = AnsiString;
  {$ifdef windows}
    //no unicode support for file names
    TFileNameString = AnsiString;
    PFileNameChar = PAnsiChar;
  {$else}
    TFileNameString = Utf8String;
    PFileNameChar = PAnsiChar;
  {$endif}
{$endif}
Аватара пользователя
Cheb
энтузиаст
 
Сообщения: 994
Зарегистрирован: 06.06.2005 15:54:34

Re: Cheb's Game Engine

Сообщение Cheb » 14.04.2017 01:33:04

Аватара пользователя
Cheb
энтузиаст
 
Сообщения: 994
Зарегистрирован: 06.06.2005 15:54:34

Re: Cheb's Game Engine

Сообщение Cheb » 15.04.2017 04:56:38

По ходу перелопатил организацию chentrah.ini , теперь очень многие настройки раскиданы по древовидной структуре, что позволило как уменьшить копипасту (можно задать один раз на все платформы) так и добиться отменной гибкости (для конкретной платформы можно выставить конкретную особенность)
Код: Выделить всё
  Reading from config [{$platform}].default_framework ...
    .. [ostRaspbian-32].default_framework ..
    .. [ostRaspbian].default_framework ..
    .. [all-linux-32].default_framework ..
    .. [all-linux].default_framework ..
    .. = "X11", found in the main INI file /home/cheb/raid2000/chentrah/chentrah.ini


Код: Выделить всё
Reading from config [{$platform}].use_gles_not_gl ...
    .. [ostRaspbian-32].use_gles_not_gl ..
    .. [ostRaspbian].use_gles_not_gl ..
    .. = "1", found in the main INI file /home/cheb/raid2000/chentrah/chentrah.ini
    .. = 1


ДЛЛы GLES на малине начали наконец грузиться. И падать СТРАШНО при инициализации.
Курю дальше исходники ZenGL.

Добавлено спустя 11 часов 44 минуты 5 секунд:
Чувствую, меня ожидает боль
Ибо то, что есть на малине, нормальной GLES явно не является, и заставить ея работать в окне - задача почище зачистки авгиевых говнюшен. :cry:
Код: Выделить всё
  First stage of EGL initialization...
  ..visual matched: depth=16, bits_per_rgb=8
  ..default color depth=16
  eglGetDisplay() returned EGL_NO_DISPLAY, trying default...
  ..display 00000001h
  ..version 1.4
  ..trying r5 g6 b5 alpha0 depth0
    ..OK
Screen rect is 1920x1080(0,0)
The window rect, as read from the config and clipped to the screen rect, should be 1103x787(320,196)
Creating the window...
  Writing to config, [video].fullscreen = "0"... Ok
  Reading from config [video].window_left ...
    .. = "0.1666666716", found in the user INI file /home/cheb/raid2000/chentrah/conf/conf.ini
    .. = 0.16666667163372
  Reading from config [video].window_top ...
    .. = "0.1815589368", found in the user INI file /home/cheb/raid2000/chentrah/conf/conf.ini
    .. = 0.181558936834335
  Reading from config [video].window_width ...
    .. = "0.5744791627", found in the user INI file /home/cheb/raid2000/chentrah/conf/conf.ini
    .. = 0.574479162693024
  Reading from config [video].window_height ...
    .. = "0.729087472", found in the user INI file /home/cheb/raid2000/chentrah/conf/conf.ini
    .. = 0.729087471961975
  the client area is 1103x787(320,196), the entire window including decorations is 1103x787(320,196)
Starting the pen tablet manager...
Listed 3 extended input devices:
  "Virtual core XTEST keyboard" (type=0, id=5)
  "No brand Combo Free KVM" (type=72, id=6)
  "No brand 4 Port KVMSwicther" (type=72, id=8)
Графический планшет: не найден (ни одно из 3 расширенных устройств ввода не распознано как планшет)
  Second stage of OpenGL ES initialization...
  Writing to config, [videocard].quality_factor = "99"... redirected to the per-videocard INI file...
   DISCARDED: OpenGL not initialized yet, device name unknown. Ok
  Writing to config, [videocard].quirks = ""... redirected to the per-videocard INI file...
   DISCARDED: OpenGL not initialized yet, device name unknown. Ok
  Writing to config, [videocard].tested_quirks = ""... redirected to the per-videocard INI file...
   DISCARDED: OpenGL not initialized yet, device name unknown. Ok
Window destroyed.
<----=* ОШИБКА! ---- (расшифровку см. ниже) *=---->
CGE initialization took 0:01.534. Entering the main loop.
Destroying engine object #1 (TLockupGuard)
  waiting for the thread to terminate...
  76790230h is /opt/vc/lib/libEGL.so, base 7677D000h
  ..file "/opt/vc/lib/libEGL.zstabs", exists=False..
  ..file "/home/cheb/raid2000/chentrah/modules/chentrah/bin/libEGL.zstabs", exists=False..
Не найдена отладочная информация Stabs для /opt/vc/lib/libEGL.so.
  76790230h is /opt/vc/lib/libEGL.so, base 7677D000h
  ..file "/opt/vc/lib/libEGL.zdwarf2", exists=False..
  ..file "/home/cheb/raid2000/chentrah/modules/chentrah/bin/libEGL.zdwarf2", exists=False..
Не найдена отладочная информация Dwarf2 для /opt/vc/lib/libEGL.so.
Оставались необработанные сообщения об ошибках:
:
  по адресу 76790230h в libEGL.so
  (нет отладочной информации: Не найдено ни одного валидного внешнего файла c отладочной информацией Dwarf2 для /opt/vc/lib/libEGL.so; функция RTL не смогла найти встроенную Stabs информацию о строках)
Стек вызовов:
  cl_main.inc:429 (run)  в debug-chentrah-armv7l
  chentrah.lpr:113 (main)  в debug-chentrah-armv7l
Аватара пользователя
Cheb
энтузиаст
 
Сообщения: 994
Зарегистрирован: 06.06.2005 15:54:34

Пред.След.

Вернуться в Разработки на нашем сайте

Кто сейчас на конференции

Сейчас этот форум просматривают: нет зарегистрированных пользователей и гости: 23

Рейтинг@Mail.ru