Před nedávnem Primoz Gabrijelcic přidal do nejnovější verze FastMM další option, který by měl vylepšit chování FastMM při mnoha vláknech. Od té doby se ozvalo několik lidí, že to s úspěchem používají na velmi zatížených serverech (např. Eric Grange na beginend.net se 180 vlákny). Původní oznámení je na googleplus - velmi zajímavé.
Pozadí: při uvolňování, resp. alokaci používá FastMM zámek, kterým chrání své struktury. Čím více je vláken, tím větší je šance, že na sebe narazí a budou soupeřit (resp. čekat na uvolnění). Primoz přišel s myšlenkou, že nejméně při uvolňování pokud se nepodaří uvolnění k vůli zámku, tak se takový blok odloží a zkusí se uvolnit při dalším volání, nebo ho uvolní nové čistící vláknou na pozadí.
Celkem mne to zaujalo a vytvořil jsem malou aplikaci s hodně vlákny, která trpí realokacemi. Bylo to první co mne napadlo jak to otestovat a je to orientační.
type
TTestThread = class (TThread)
public
procedure Execute; override;
end;
implementation
{$R *.dfm}
procedure TfDemo.Button1Click(Sender: TObject);
var
i: Integer;
begin
for i := 1 to 50 do
begin
TTestThread.Create(False).FreeOnTerminate := True;
end;
end;
{ TTestThread }
procedure TTestThread.Execute;
var
s: string;
I: Integer;
begin
for I := 1 to 1000000 do
s := s + IntToStr(i);
end;
V podstatě program vytvoří 50 vláken a v každém je spousta realokací řetězce (tj. hodně možností na kolizi). Test je to velmi orientační, ale hodně práce s řetězci simuluje web server. Všechno bylo testováno na Delphi XE a šlo mi spíše o charakteristiku průběhu.
Zkusil jsem otestovat FastMM z instalace (Delphi XE jako taková klasika), aktuální verzi FastMM ve třech módech, vanilla Delphi 5 a nahrazení správce paměti za volání Windows API.
Delphi 5
Delphi 5 (nebo i 7) používá původní správce paměti, který exceloval kolem Windows 95 pro normální aplikace. Dnes ho snad už nikdo nepoužívá, protože nejméně při delším používání (služby, server) trpí fraqmentací.
Po minutě mne to přestalo bavit a proces jsem zabil. Paměť konstantní, vytížení všech jader tak na půl.
Windows
Před FastMM bylo pro služby doporučováno nahradit správce paměti za přímé volání Windows API, protože od cca Windows 2000 se správce velmi zlepšil a i dnes to někdo doporučuje. Použil jsem gebyho memory manager.
Po minutě mne to přestalo bavit a proces jsem zabil. Paměť konstantní, vytížení jader mnohem lepší a s jinou charakteristikou. Celkem ale tedy zklamání (Windows 7 64bit), buďto si to nesedlo s mým testem, nebo nevím.
Výchozí správce z XE
Poslední verze před podporou 64bit.
Not bad. Hlavně to během pár sekund doběhlo, což je plus. Vytížení procesoru nic moc a hrbolek s pamětí ukazuje malé zvýšení.
FastMM ve výchozí konfiguraci měl stejnou charakteristiku, jen byl efektivnější. Jinak předpokládám, že pro novější Delphi je to čím dál lepší, protože přejímají novější verzi FastMM. Takže se vyplatí použít nejméně aktuální verzi FastMM.
NeverSleepOnThreadContention
Jedna z možností jak FastMM poladit je nastavit NeverSleepOnThreadContention, ale to má zlepšit výkon u menšího počtu vláken, protože to při nalezení zámku nepřejde
do Sleep, ale aktivně čeká. Tohle mohlo výrazně vylepšit výkon, nebo taky ne.
A protože je vláken moc, tak to dopadlo špatně. Všechno vytočené na max, ale trvá to (to je to aktivní čekání).
UseSwitchToThread
Vylepšení předchozího: místo aktivního čekání zavolá SwitchToThread a doufá, že konkurent brzo skončí se zámkem. Vhodné pro mnoho jader nebo mnoho vláken.
Lepší předchozí. SwitchToThread evidentně zabral (pozn. Windows 2000+).
UseReleaseStack
To je to oč mi vlastně šlo, princip jsem popsal dříve.
{$define UseReleaseStack}
Výsledek není vůbec špatný a shoduje se s ukázkami jiných.
Nechtěl jsem měřit přesné časy, protože je to ošemetné. Ale charakteristiky jsou celkem vypovídající.