Главная страница
    Top.Mail.Ru    Яндекс.Метрика
Форум: "Прочее";
Текущий архив: 2006.11.19;
Скачать: [xml.tar.bz2];

Вниз

Динамическое выделение памяти на стеке   Найти похожие ветки 

 
Alex Konshin ©   (2006-10-28 11:38) [0]

Я тут велосипед избрел...
Вроде как научился безопасно и быстро выделять память на стеке.
Смотрел на полученный код и тестировал разные ситуации, вроде подвохов не обнаружил. Может вы чего нароете?

Для чего это нужно? Удобно портировать C/C++ код в котором место под небольшие временые массивы с неконстантным размером запросто выделяется на стеке простым объявлением, и потому очень часто используется. Брать-отдавать память - медлено. Вот я и придумал вроде как приемлемый способ это сделать.

Как это работает? Во-первых, я надеюсь, что это действительно работает:).
Конструкция try...finally создает frame на стеке. При выходе по Exit или при исключении этот фрейм откатывается и указательстека восстанавливается автоматом. Поэтому нам нужно позаботится только об восстановлении указателя стека при нормальном входе из try...finally.  Естественно, выделять память нужно кусками, кратными 4. Собственно, вот и вся хитрость.


procedure NN_ModInv( a, b, c: PDigits; digits : LongWord );
var
 q, t1, t3, u1, u3, v1, v3, w : PDigits;
 old_esp : Pointer;
begin
 // Do something
 ...

 try
   // Allocate temp space on stack
   asm
     mov old_esp,esp
     mov eax,digits
     shl eax,2
     sub esp,eax
     mov q,esp
     sub esp,eax
     mov t1,esp
     sub esp,eax
     mov t3,esp
     sub esp,eax
     mov u1,esp
     sub esp,eax
     mov u3,esp
     sub esp,eax
     mov v1,esp
     sub esp,eax
     mov v3,esp
     sub esp,eax
     sub esp,eax
     mov w,esp
   end;

   // Do something
   ...
   ...

   asm
     mov esp,old_esp
   end;
 finally
   // Restore stack pointer.
   // try...finally creates stack frame.
 end;
end;


 
Alex Konshin ©   (2006-10-28 11:56) [1]

А, забыл сказать, что если используется Exit, то перед ним указатель стека тоже нужно восстанавливать ( asm mov esp,old_esp end; ).

Ну и понятно, что все это имеет смысл, когда скорость выполнения очень критична. В другом случае можно найти способ и по-проще :)


 
Суслик ©   (2006-10-28 12:39) [2]


> Alex Konshin ©   (28.10.06 11:56) [1]

глупый вопрос можно?
ты уверен, что оптимизатор не заоптимизирует пустой try-finally.
такой гарантии имхо никто дать не может.


 
Суслик ©   (2006-10-28 12:40) [3]

вернее пустой finally end.


 
Суслик ©   (2006-10-28 12:47) [4]

я, конечно, не специалист, но почему бы непереместить восстановление стека в finally end?


 
Ketmar ©   (2006-10-28 14:16) [5]

>[0] Alex Konshin(c) 28-Oct-2006, 11:38
бр-р-р... пугаешь спросонья. %-) способ вполне. только надо добавить для копипастеров, что если большие объёмы -- не прокатит. надо будет последовательно странички "трогать". %-)


 
Суслик ©   (2006-10-28 14:28) [6]

господа, просветите, пожалуйста, непросвещенных про странички - ну так, хотя бы в общем.:)


 
Ketmar ©   (2006-10-28 14:54) [7]

>[6] Суслик(c) 28-Oct-2006, 14:28
>господа, просветите, пожалуйста, непросвещенных про
>странички - ну так, хотя бы в общем.:)
память для стека выделяется не сразу толпой, а постранично (кажись, по 4 кб; может, в новых системах по 64 или около того -- не в курсе). на страничку, которая идёт перед рабочей, ставится флажок "GUARD". если к ней обращаются, возникает исключение. система его ловит, и выделяет ещё для стека кусочек. и так далее. но! если выделить, например, 16 килобайт за раз, есть шанс попасть мимо "охраняемой" странички. и поймать исключение, на которое система уже не среагирует. т.е. банальный AV. поэтому надо выделять по 4 кила, и каждый четырёхкилобайтный кусочек "трогать" (т.е. читать оттуда что-то, например). этим ты гарантируешь работоспособность своей программы на любой вменяемой системе.


 
iZEN ©   (2006-10-28 15:11) [8]

В Java 6.0 (Mustang) объекты с ограниченной обласьтю видимости (внутри методов) инстанцируются на стэке. Это повышает производительность в разы. ;)


 
wicked ©   (2006-10-28 16:03) [9]


> В Java 6.0 (Mustang) объекты с ограниченной обласьтю видимости
> (внутри методов) инстанцируются на стэке.

наконец то, дошли...

еще бы счетчик ссылок на обьекты в куче - и gc бы ловил только циклически ссылающиеся обьекты - еще существенный прирост производительности...


 
Alex Konshin ©   (2006-10-28 16:19) [10]

Прежде чем гадать, просто посмотри код.

> Суслик ©   (28.10.06 12:47) [4]
> я, конечно, не специалист, но почему бы непереместить восстановление
> стека в finally end?

То, что внутри finally...end оформляется как процедурка, которая вызывается при откате фрейма. Так что там уж точно не нужно трогать стек.


 
default ©   (2006-10-28 16:22) [11]

кстати, можно создать класс управляющий стеком, то есть предоставлять высокоуровневое пользование стека
надо только подумать есть ли в этом смысл
вот пример

TStackManager = class
    procedure NextStackAlloc(var P: Pointer; countDWords: LongWord);
 end;
...

procedure TStackManager.NextStackAlloc(var P: Pointer; countDWords: LongWord);
asm
    shl countDWords, 2
    sub countDWords, 4
    add esp, countDWords
    mov [P], esp
    jmp [esp+countDWords]
end;


NextStackAlloc выделят указанное число двойных слов в стеке и записывает адрес на него в P
идея проста - получаем
UserStackDWord
....
UserStackDWord
UserStackDWord
RET ADDRESS

далее JMP-ом прыгаем на RET ADRESS тем самым после выполения метода мы получаем кусок стека и переменную указывающую на него и ничего другого


 
Alex Konshin ©   (2006-10-28 16:27) [12]

Хитрость использования вроде бы ненужного try...finally в том, что создается фрейм, что гарантирует правильный откат стека при исключениях и Exit.


 
default ©   (2006-10-28 16:31) [13]

RET ADDRESS - это тоже часть юзерской памяти стека
не add esp, countDWords, а  
   sub esp, countDWords конечно


 
Alex Konshin ©   (2006-10-28 16:31) [14]

> default ©   (28.10.06 16:22) [11]

Да выделить-то не проблема. Я в ассемблере это частенько делаю.
Проблема была не обломиться при исключениях.


 
default ©   (2006-10-28 16:43) [15]

Alex Konshin ©   (28.10.06 16:31) [14]
всё-таки может лучше классом оформить - добавить ещё в него метод для освобождения всей выделенной памяти
тогда всё низкоуровневое будет скрыто
а то при каждой портации код будет пестреть asm кодом что выглядит не очень:)


 
Ketmar ©   (2006-10-28 17:20) [16]

>[14] Alex Konshin(c) 28-Oct-2006, 16:31
>Проблема была не обломиться при исключениях.
в конце делаем принудительный raise.
а все вместе заключаем в try try except finally.
смотрим и наслаждаемся "ужосом". %-))


 
iZEN ©   (2006-10-28 18:20) [17]


> wicked ©   (28.10.06 16:03) [9]
> > В Java 6.0 (Mustang) объекты с ограниченной обласьтю видимости
> > (внутри методов) инстанцируются на стэке.
>
> наконец то, дошли...
>
> еще бы счетчик ссылок на обьекты в куче - и gc бы ловил
> только циклически ссылающиеся обьекты - еще существенный
> прирост производительности...

У GC HotSpot JVM с рождения нету никаких счётчиков (в отличие от OLE/COM и VB).

Обычные (односторонние) и циклические (образующие кольцо) ссылки обрабатываются по стратегии "поколений".


 
Суслик ©   (2006-10-28 18:56) [18]


> Ketmar ©   (28.10.06 14:54) [7]

спасибо


> Alex Konshin ©   (28.10.06 16:19) [10]
> Прежде чем гадать, просто посмотри код.

Это ты мне? Я, действительно, не очень знаю как там все устроено, но знаю одно - потимизатор штука коварная.

Ты все-таки задумайтся о том, что дельфи волен оптимизировать пустой finally end. Он не подписывался на то, что оптимизировать не это не будет. Хотя я могу быть и неправ? Но я в доках этого не видел.


 
default ©   (2006-10-28 19:50) [19]


 TStackManager = class
 private
   FCountBytes: LongWord;
 public
   procedure NextStackAlloc(var P: Pointer; countDWords: LongWord);
   procedure Clear;
end;

...

procedure TStackManager.NextStackAlloc(var P: Pointer; countDWords: LongWord);
asm
   shl countDWords, 2
   add Self.FCountBytes, countDWords
   sub countDWords, 4
   sub esp, countDWords
   mov [P], esp
   jmp [esp+countDWords]
end;

procedure TStackManager.Clear;
asm
  pop ecx
  add esp, Self.FCountBytes
  jmp ecx
end;


вот пример менеджера стека
можете пользовать кому интересно


 
jack128 ©   (2006-10-28 20:07) [20]

Alex Konshin ©   (28.10.06 11:38)
Динамическое выделение памяти на стеке


unit Grids;

function StackAlloc(Size: Integer): Pointer; register;
procedure StackFree(P: Pointer); register;


 
Суслик ©   (2006-10-28 20:19) [21]


> jack128 ©   (28.10.06 20:07) [20]

чисто теоретический вопрос - а кто восстанавливает SP в случае исключения в методе TCustomGrid.Paint?


 
jack128 ©   (2006-10-28 20:25) [22]

Суслик ©   (28.10.06 20:19) [21]
а фиг его знает.. Может борланды подумали, что исключения там настолько маловероятно, что на них не нужно закладываться..


 
Суслик ©   (2006-10-28 20:28) [23]


> jack128 ©   (28.10.06 20:25) [22]

я когда писал свой грид по мотивам grids (просто взял борландовый грид и отрезал лишнее - для меня - потом добавил требуемое), то это место еще тогда вопрос вызвало. но тогда я на это забил.


 
Alex Konshin ©   (2006-10-29 00:31) [24]

> jack128 ©   (28.10.06 20:07) [20]
> Alex Konshin ©   (28.10.06 11:38)
> Динамическое выделение памяти на стеке
>
> unit Grids;
>
> function StackAlloc(Size: Integer): Pointer; register;
> procedure StackFree(P: Pointer); register;

Я об этом, естественно, знал. Я же в свое время написал ArrayGrids и потому Grids  знал почти наизусть. И есть у меня сомнения, что это будет корректно работать c Exit, точнее, я знаю, что будет работать некорректно - смотри System._TryFinallyExit. Эта процедурка отрабатывает по Exit внутри try...finally. Как можно заметить, она явно ожидает стекфрейм сразу за адресом возврата.

Я знаю, что выделять память на стеке можно. Собственно, нового в моем фрагменте только то, как сделать это безопасно для исключений и Exit.


 
Alex Konshin ©   (2006-10-29 00:34) [25]


> default ©   (28.10.06 19:50) [19]
>
>
>  TStackManager = class
>  private
>    FCountBytes: LongWord;
>  public
>    procedure NextStackAlloc(var P: Pointer; countDWords:
>  LongWord);
>    procedure Clear;
> end;
>
> ...
>
> procedure TStackManager.NextStackAlloc(var P: Pointer; countDWords:
>  LongWord);
> asm
>    shl countDWords, 2
>    add Self.FCountBytes, countDWords
>    sub countDWords, 4
>    sub esp, countDWords
>    mov [P], esp
>    jmp [esp+countDWords]
> end;
>
> procedure TStackManager.Clear;
> asm
>   pop ecx
>   add esp, Self.FCountBytes
>   jmp ecx
> end;
>
>
> вот пример менеджера стека
> можете пользовать кому интересно


 
Суслик ©   (2006-10-29 00:42) [26]


> Alex Konshin ©   (29.10.06 00:31) [24]
> Я же в свое время написал
> ArrayGrids и потому Grids  знал почти наизусть.

ОФФ
Если так, то скажи, пожалуйста, что делает

procedure TInplaceEdit.Invalidate;
var
 Cur: TRect;
begin
 ValidateRect(Handle, nil);
 InvalidateRect(Handle, nil, True);
 Windows.GetClientRect(Handle, Cur);
 MapWindowPoints(Handle, Grid.Handle, Cur, 2);
 ValidateRect(Grid.Handle, @Cur);
 InvalidateRect(Grid.Handle, @Cur, False);
end;

Не по семантике, которая описана в хелпе, а по строчкам.
Конкретный вопрос

Зачем первой строкой
  ValidateRect(Handle, nil);
А второй
  InvalidateRect(Handle, nil, True);


 
Alex Konshin ©   (2006-10-29 00:59) [27]

Извиняюсь, случайно отправил.

Наворачивать тут класс уж явно не к месту. Создание объекта - это уже обращение к менеджеру памяти. А если уж всеравно обращаемся, то тогда и можно и временую память брать по GetMem. Ну да, можно, конечно, объект завести один и потом им одним пользоваться, но тогда нужно следить за вложенными вызывами.
Теоретически, я думаю, можно подумать в сторону методов в описании Record (вроде как это теперь можно в BDS2006).
Тогда можно будет написать что-то типа:

type
  TDynStackManager = record
     Size : LongWord;
     old_esp : pointer;
     function Allocate( Size : LongWord ) : Pointer;
     procedure Free;
  end;

procedure Test();
var
 stackManager : TDynStackManager;
 temp : Pointer;
begin

 try
    temp := stackManager.Allocate(10);
   
    // Do something

    stackManager.Free;
 finally
 end;

end;


Вообще я так смотрю, что они довольно много добавили в язык в BDS2006.
Кстати, мое исследование показывает, что теоретически не сложно добавить в язык и динамическое массивы на стеке. Правда, этого мы уже не увидим.


 
Alex Konshin ©   (2006-10-29 01:04) [28]

> Суслик ©   (29.10.06 00:42) [26]
>
> > Alex Konshin ©   (29.10.06 00:31) [24]
> > Я же в свое время написал
> > ArrayGrids и потому Grids  знал почти наизусть.

Ну, тут прямо слово не скажи :) Говорю же что знал. Но разобраться, если надо, смогу быстро. Извини, но не сейчас и не в этой ветке, пож-ста.


 
Суслик ©   (2006-10-29 01:05) [29]


> Вообще я так смотрю, что они довольно много добавили в язык
> в BDS2006.

ошибок пока только очень много. особенно в записях с методами.


> Правда, этого мы уже не увидим.

почему?

--------
они хотят в язык еще очень много добавить - например неявные инициализаторы и файнализаторы для записей (аналоги существующих сейчас system._initializerecord и system._finalizerecord).
Вот жизнь начнется - я думаю, что появятся разработки GC для DelphiWin32 на основе указанной возможности :)


 
Суслик ©   (2006-10-29 01:06) [30]


> Alex Konshin ©   (29.10.06 01:04) [28]
> > Суслик ©   (29.10.06 00:42) [26]
> Ну, тут прямо слово не скажи :) Говорю же что знал. Но разобраться,
>  если надо, смогу быстро. Извини, но не сейчас и не в этой
> ветке, пож-ста.

хорошо. завтра в winapi задам, т.к. вопрос к winapi в основном относится.
буду рад если кто-нить ответит ибо я не понимаю.


 
default ©   (2006-10-29 01:25) [31]

Alex Konshin ©   (29.10.06 00:59) [27]
это понятно, класс это так - если  понадобятся ещё какие фичи для работы со стеком
а так что избежать асмовых инструкций при каждой портации можно просто две подпрограммки написать и одну глобальную переменную заиметь для них
всё-таки лучше читаться код будет - особенно теми кого пугает асм
особенно если требуется расбрасывать аллокации по коду
не будем же мы писать 10 строчек на дельфи потом asm end десять строчек на дельфи - asm end; :)


 
Alex Konshin ©   (2006-10-29 01:33) [32]

> default ©   (29.10.06 01:25) [31]
> не будем же мы писать 10 строчек на дельфи потом asm end
> десять строчек на дельфи - asm end; :)

Собственно, я так сейчас и делаю. Для меня в этом проектике скорость принципиально важна - я с криптографией балуюсь.

Record будет лучше - он тут более к месту, т.к. он будет на стеке. Правда это сделать можно только в BDS.
Глобальную переменную нельзя - не будет реентерабельности.


 
Суслик ©   (2006-10-29 01:40) [33]


> Правда это сделать можно только в BDS.

а просто рекорд и глобальная функция, работающая с record"ом?


 
Alex Konshin ©   (2006-10-29 01:56) [34]

> Суслик ©   (29.10.06 01:40) [33]
> > Правда это сделать можно только в BDS.
>
> а просто рекорд и глобальная функция, работающая с record"ом?

Это завсегда можно, но намного менее красиво (О, как сказал!) :)


 
Суслик ©   (2006-10-29 02:06) [35]


> Alex Konshin ©   (29.10.06 01:56) [34]

Вообще BDS до устойчивой работы компилятора НЕ в демах, а в рабочих проектах в области НОВЫХ фишек еще далеко. С записями и их методами, действительно, много багов.

А вот перегрузка операторов работает отлично. Сам иногда поражаюсь - как можно наплодить столько багов в задаче, которая в первого взглада не видитсья сложной (методы в записях) и суметь сделать классно перегрузку операторов в тех же записях.

Такое ощущение, что там несколько неависимых команд работает :)

PS. Это я все к тому, что *пока* красота в записях еще не совсем красота.


 
Ketmar ©   (2006-10-29 02:11) [36]

>[35] Суслик(c) 29-Oct-2006, 02:06
>и суметь сделать классно перегрузку операторов
раскоментарили старый код, оставшийся от C++. %-)


 
Суслик ©   (2006-10-29 02:15) [37]


> Ketmar ©   (29.10.06 02:11) [36]

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


 
Ketmar ©   (2006-10-29 02:19) [38]

>[37] Суслик(c) 29-Oct-2006, 02:15
>иногда прослеживалось то, что компилятор делал до 100
>операций прежде чем сложить мне две записи - находил
>цепочки неявных преобразований, приводивших к тому, что
>находилась запись, которая моглы выполнить сложение.
я под столом. буквально. стул развалился. смеялся я сильно. я вообще к перегрузкам отношусь так же, как Вирт -- потому и смеялся.


 
Суслик ©   (2006-10-29 02:29) [39]


> Ketmar ©   (29.10.06 02:19) [38]

смейся обратно.
как принимающий участие в qc.borland.com (в отличие от вас всех, любители поговорить) могу сказать, что я это делал исключительно в целях тестирования - заявленная фича должна работать независимо от уровня идиотизма программируещего.


 
Ketmar ©   (2006-10-29 02:36) [40]

>[39] Суслик(c) 29-Oct-2006, 02:29
>смейся обратно.
я опять не так выразился. я смеюсь не по поводу её работоспособности или нет. а по поводу самого факта её наличия. и вытекающих.



Страницы: 1 2 вся ветка

Форум: "Прочее";
Текущий архив: 2006.11.19;
Скачать: [xml.tar.bz2];

Наверх





Память: 0.57 MB
Время: 0.061 c
1-1160550411
zorik
2006-10-11 11:06
2006.11.19
Внутреннему класу узнать параметр класа в который он входит


15-1162462262
psa247
2006-11-02 13:11
2006.11.19
Договор купли-продажи программы


15-1162206003
maxmax111
2006-10-30 14:00
2006.11.19
поскажите программу, которая..


1-1160032506
DVM
2006-10-05 11:15
2006.11.19
OnShow у TFrame как сделать?


15-1162451743
StriderMan
2006-11-02 10:15
2006.11.19
Delphi + FreeBSD. Возможно ли?





Afrikaans Albanian Arabic Armenian Azerbaijani Basque Belarusian Bulgarian Catalan Chinese (Simplified) Chinese (Traditional) Croatian Czech Danish Dutch English Estonian Filipino Finnish French
Galician Georgian German Greek Haitian Creole Hebrew Hindi Hungarian Icelandic Indonesian Irish Italian Japanese Korean Latvian Lithuanian Macedonian Malay Maltese Norwegian
Persian Polish Portuguese Romanian Russian Serbian Slovak Slovenian Spanish Swahili Swedish Thai Turkish Ukrainian Urdu Vietnamese Welsh Yiddish Bengali Bosnian
Cebuano Esperanto Gujarati Hausa Hmong Igbo Javanese Kannada Khmer Lao Latin Maori Marathi Mongolian Nepali Punjabi Somali Tamil Telugu Yoruba
Zulu
Английский Французский Немецкий Итальянский Португальский Русский Испанский