function CommittedStackSize: Cardinal;
asm
mov eax,[fs:$4] // base of the stack, from the Thread Environment Block (TEB)
mov edx,[fs:$8] // address of lowest committed stack page
// this gets lower as you use more stack
sub eax,edx
end;
{***********************************************************
StackUse - A unit to report stack usage information
by Richard S. Sadowsky
version 1.0 7/18/88
released to the public domain
Inspired by a idea by Kim Kokkonen.
This unit, when used in a Turbo Pascal 4.0 program, will
automatically report information about stack usage. This is very
useful during program development. The following information is
reported about the stack:
total stack space
Unused stack space
Stack spaced used by your program
The unit's initialization code handles three things, it figures out
the total stack space, it initializes the unused stack space to a
known value, and it sets up an ExitProc to automatically report the
stack usage at termination. The total stack space is calculated by
adding 4 to the current stack pointer on entry into the unit. This
works because on entry into a unit the only thing on the stack is the
2 word (4 bytes) far return value. This is obviously version and
compiler specific.
The ExitProc StackReport handles the math of calculating the used and
unused amount of stack space, and displays this information. Note
that the original ExitProc (Sav_ExitProc) is restored immediately on
entry to StackReport. This is a good idea in ExitProc in case a
runtime (or I/O) error occurs in your ExitProc!
I hope you find this unit as useful as I have!
************************************************************)
{$R-,S-} { we don't need no stinkin range or stack checking! }
unit StackUse;
interface
var
Sav_ExitProc : Pointer; { to save the previous ExitProc }
StartSPtr : Word; { holds the total stack size }
implementation
{$F+} { this is an ExitProc so it must be compiled as far }
procedure StackReport;
{ This procedure may take a second or two to execute, especially }
{ if you have a large stack. The time is spent examining the }
{ stack looking for our init value ($AA). }
var
I : Word;
begin
ExitProc := Sav_ExitProc; { restore original exitProc first }
I := 0;
{ step through stack from bottom looking for $AA, stop when found }
while I < SPtr do
if Mem[SSeg:I] <> $AA then begin
{ found $AA so report the stack usage info }
WriteLn('total stack space : ',StartSPtr);
WriteLn('unused stack space: ', I);
WriteLn('stack space used : ',StartSPtr - I);
I := SPtr; { end the loop }
end
else
inc(I); { look in next byte }
end;
{$F-}
begin
StartSPtr := SPtr + 4; { on entry into a unit, only the FAR return }
{ address has been pushed on the stack. }
{ therefore adding 4 to SP gives us the }
{ total stack size. }
FillChar(Mem[SSeg:0], SPtr - 20, $AA); { init the stack }
Sav_ExitProc := ExitProc; { save exitproc }
ExitProc := @StackReport; { set our exitproc }
end.
6条答案
按热度按时间yfjy0ee71#
使用这个来计算为当前线程堆栈提交的内存量:
字符串
又是一个我没有的主意。
qni6mghb2#
为了完整起见,我添加了opc0de's answer中提供的
CommittedStackSize
函数的一个版本,用于 * 确定使用的堆栈数量 *,该函数适用于x86 32位和64位版本的Windows(opc 0 de的函数仅适用于Win32)。opc 0 de的函数从Windows的Thread Information Block (TIB)中查询堆栈基址和最低提交堆栈基址的地址。x86和x64之间有两个区别:
FS
段寄存器指向,但在Win 64上由GS
指向(参见here)另外请注意,BASM代码中有一个小的差异,因为在x64上,需要
abs
来使汇编程序使用段寄存器的绝对偏移量。因此,将在Win32和Win 64版本上工作的版本看起来像这样:
字符串
j2qf4p5b3#
我记得几年前,我在init时用零填充了所有可用的堆栈空间,并在deinit时从末尾开始计算连续的零。这产生了一个很好的“高水位”,只要你把你的应用程序通过它的速度进行探测运行。
等我不动了回来我会找出密码的。
更新:好的,这个原理在这个(古老的)代码中演示:
字符串
(From http://webtweakers.com/swag/MEMORY/0018.PAS.html)的数据库
我依稀记得当时曾与Kim Kokkonen合作过,我认为原始代码来自他。
这种方法的好处是在程序运行期间没有性能损失,也没有分析操作。只有在关闭时,找到更改值之前的循环代码才会占用CPU周期。(我们稍后在汇编中对它进行了编码。)
wswtfjt74#
即使所有8个线程都接近使用它们的1 MB堆栈,也只有8 MB的虚拟内存。IIRC中,线程的默认初始堆栈大小为64 K,除非达到进程线程堆栈限制,否则会在页面错误时增加,此时我假设您的进程将通过“堆栈溢出”消息框停止:((
我担心减少进程堆栈限制$MAXSTACKSIZE不会减轻您的碎片/分页问题,如果有的话。你需要更多的RAM,这样你的mega-photo-app的常驻页面集就更大了&从而减少了抖动。
在您的进程中,总体上平均有多少个线程?任务管理器可以显示这一点。
Rgds,Martin
to94eoyn5#
虽然我确信你可以减少应用程序中的线程堆栈大小,但我不认为这会解决问题的根本原因。您现在使用的是8核机器,但在16核或32核等上会发生什么呢?
使用32位 Delphi ,您的最大地址空间为4GB,因此这确实在某种程度上限制了您。您可能需要为部分或全部线程使用较小的堆栈,但在足够大的机器上仍然会遇到问题。
如果要帮助您的应用更好地扩展到更大的计算机,您可能需要采取以下一个或其他步骤:
1.避免创建比内核多得多的线程。使用插件可用的线程池体系结构。如果没有.net环境的好处来简化这一点,您将最好针对Windows线程池API进行编码。也就是说,必须有一个好的 Delphi Package 器可用。
1.处理内存分配模式。如果你的线程正在分配200MB区域内的连续块,那么这将给你的分配器带来不必要的压力。我发现,通常最好将如此大量的内存分配在较小的固定大小的块中。这种方法可以解决您遇到的碎片问题。
i2byvkas6#
减少$MAXSTACKSIZE不会起作用,因为Windows总是将线程堆栈对齐为1 Mb(?)).
一个(可能?)防止碎片的方法是保留(而不是分配!)虚拟内存(使用VirtualAlloc)。并在线程运行后释放它。这样Windows就不能使用为线程保留的空间,所以你将有一些连续的内存。
或者你可以为大照片制作自己的内存管理器:手动从该池中保留大量虚拟内存和分配内存。(您需要自己维护一个已用和已用内存的列表)。
至少,这是一个理论,不知道它是否真的工作...