As I wrote in
a previous post, Delphi string, dynamic array and memory manager don't like
multi-core CPU.
My proposal is to add a threadlocalvar keyword, to be used instead
of var in your code, to mark some variables to be used in only the
current thread. Then the compiler and RTL won't have to use the LOCK
instruction, and the application will be MUCH faster in multi-thread
environment.
I made this proposal in both the Embarcadero
forum and in the FreePascal forum.
And what about using a
threadlocalvar new reserved word, in a
threadvar way, which could define the variable (string, dynamic array)
to be accessed by the current thread only, and won't have any LOCK call?
My guess is that any server thread uses mostly such local variables (e.g.
for string concatenation of XML or HTML stream), and don't need the thread safe
approach most of the time.
It could lead into some confusion for less advanced Delphi users, but the
threadvar itself does exist for years, but is used only by those how
need this feature. If you need it, you use it. Otherwise you ignore it, and
your application will be slower in some cases, but wil work. For some CPU
intensive applications, like multi-thread servers, it should be a great
improvement to have such a threadlocalvar at hand.
Note 1: the threadvar implements one variable instance per
thread, and the threadlocalvar should implements one variable instance
for the current thread only.
Note 2: any threadlocalvar could be assigned to a normal var,
when a multi-thread safe variable is needed (e.g. for communication between
threads): in this case, it will be a direct write, not a copy on write. Copy on
write would be enabled between threadlocalvar, of course, for better
performance.
Note 3: it's up to the programmer to take care of the multi-thread approach,
but writing threadlocalvar (so many characters to type!) will prevent for doing
it without knowing it.
This could be something like that:
function TServer.ComputePage: string;
threadlocalvar
tmp: string; // differs from var tmp: string
i: integer; // i is defined as threadlocalvar, but is the same as normal var
begin
for i := 0 to high(ListStr) do // ListStr[] is a dynamic array
tmp := tmp+ListStr[i]+#13#10; // very fast computation, without LOCK
result := tmp; // copy from local heap to global heap
end;
To implement this at the compiler and RTL level, we could use reference
count <=-2 for these variables (-1=const, -2=ref 0, -3=ref 1..).
The RTL (i.e. system.pas unit) should be modified as such, for example for
handling a threadlocalvar string reference counting add:
function _LStrAddRef(var str): Pointer;
var P: PStrRec;
begin
if Integer(str)<>0 then begin
P := Pointer(Integer(str) - sizeof(StrRec));
if P.refcnt >= 0 then
InterlockedIncrement(P.refcnt) else // slow multi-thread safe reference count
if P.refcnt < -1 then // -1 for const
dec(P.refcnt); // -2,-3... for threadlocalvar
end;
Result := Pointer(str);
end;
And a local threadheap should be implemented for such threadlocalvar, with
threadlocalgetmem() and such functions.
Another possibility should be do add a "threadlocal" attribute for
types, to be used for local variables and properties:
TServer = class
public
ListStr: threadlocal array of string;
...
In this case, the method above should begin with:
function TServer.ComputePage: string;
var
tmp: threadlocal string; // differs from var tmp: string
....
But this syntax sound a bit not "pascalish", whereas the threadlocalvar does
(it sounds like the treadvar feature) ... another idea?
Comments and ideas are welcome on our forum.