I was reading the source code of the FastMM4 and this short function came to my eyes.
function GetThreadID: Cardinal; {$ifdef 32Bit} asm mov eax, FS:[$24] end; {$else} begin Result := GetCurrentThreadID; end; {$endif}
I guess from the {$ELSE} block that fetching value from segment FS:[$24] should be the same as returning the thread ID. The C++ version is similar.
1 2 3 4 5 6 7 8 9 10 11 | unsigned int GetThreadID(void) { __asm { mov eax, FS:[$24] } } unsigned int GetProcessID(void) { __asm { mov eax, FS:[$20] } } |
unsigned int GetThreadID(void) { __asm { mov eax, FS:[$24] } } unsigned int GetProcessID(void) { __asm { mov eax, FS:[$20] } }
Then I asked for more explanation in stackoverflow.com and as always, I obtained the answer. The TIB (Thread Information Block) can be accessed using FS register on x86 platforms. The value at FS+$24 stores the thread ID and I can see that FS+$20 stores the process ID. The register eax on x86 calling convention stores the return value (32-bit signed/unsigned integer).
It is unlikely that getting Thread ID or Process ID will be the bottleneck of the applications since these two IDs will not changed (you just get the values and store them once for further usages). But I ‘d like to know in extreme case, how fast would this be compared to the standard ones defined in Windows unit.
function GetCurrentProcessId; external kernel32 name 'GetCurrentProcessId'; function GetCurrentThreadId; external kernel32 name 'GetCurrentThreadId';
So we can easily come up the performance comparison console application (written in Delphi XE3).
program Test; {$APPTYPE CONSOLE} uses Windows; function GetThreadID: Cardinal; register; assembler; asm mov eax, FS:[$24] end; function GetProcessID: Cardinal; register; assembler; asm mov eax, FS:[$20] end; var i: integer; x: Cardinal; t: DWORD; begin Writeln(GetThreadID = Windows.GetCurrentThreadId); Writeln(GetProcessID = Windows.GetCurrentProcessId); ///////////// thread id ///////////////////// t := GetTickCount; for i := 1 to 1000000000 do begin x := GetThreadID; end; Writeln(GetTickCount - t); t := GetTickCount; for i := 1 to 1000000000 do begin x := Windows.GetCurrentThreadId; end; Writeln(GetTickCount - t); /////////// process id /////////////////////////////// t := GetTickCount; for i := 1 to 1000000000 do begin x := GetProcessID; end; Writeln(GetTickCount - t); t := GetTickCount; for i := 1 to 1000000000 do begin x := Windows.GetCurrentProcessId; end; Writeln(GetTickCount - t); Readln; end.
And we have the following results.
We can see that the above assembly versions are at least two times faster. The performance difference thus is said to be the function invoke overhead i.e. the Windows unit invokes the WinAPI in kernel32.dll.
References:
1. http://stackoverflow.com/questions/16173878/getthreadid-in-assembly
2. http://en.wikipedia.org/wiki/Win32_Thread_Information_Block
–EOF (The Ultimate Computing & Technology Blog) —
loading...
Last Post: Teaching Kids Programming - Estimate the Golden Ratio via Fibonacci Numbers in Python
Next Post: Embed Images in HTML with API support