Groups | Search | Server Info | Keyboard shortcuts | Login | Register [http] [https] [nntp] [nntps]
Groups > comp.lang.forth > #55159 > unrolled thread
| Started by | Ron Aaron <rambamist@gmail.com> |
|---|---|
| First post | 2017-02-27 07:30 +0200 |
| Last post | 2017-02-28 03:50 -0500 |
| Articles | 20 on this page of 23 — 8 participants |
Back to article view | Back to comp.lang.forth
Any ideas on faster TLS ? Ron Aaron <rambamist@gmail.com> - 2017-02-27 07:30 +0200
Re: Any ideas on faster TLS ? Andrew Haley <andrew29@littlepinkcloud.invalid> - 2017-02-27 04:01 -0600
Re: Any ideas on faster TLS ? Ron Aaron <rambamist@gmail.com> - 2017-02-27 13:19 +0200
Re: Any ideas on faster TLS ? Alex <alex@rivadpm.com> - 2017-02-27 12:56 +0000
Re: Any ideas on faster TLS ? Ron Aaron <rambamist@gmail.com> - 2017-02-27 15:10 +0200
Re: Any ideas on faster TLS ? albert@cherry.spenarnc.xs4all.nl (Albert van der Horst) - 2017-02-27 17:08 +0100
Re: Any ideas on faster TLS ? Andrew Haley <andrew29@littlepinkcloud.invalid> - 2017-02-27 08:06 -0600
Re: Any ideas on faster TLS ? Ron Aaron <rambamist@gmail.com> - 2017-02-27 16:10 +0200
Re: Any ideas on faster TLS ? Andrew Haley <andrew29@littlepinkcloud.invalid> - 2017-02-27 09:31 -0600
Re: Any ideas on faster TLS ? Ron Aaron <rambamist@gmail.com> - 2017-02-28 07:05 +0200
Re: Any ideas on faster TLS ? Paul Rubin <no.email@nospam.invalid> - 2017-02-27 23:15 -0800
Re: Any ideas on faster TLS ? Andrew Haley <andrew29@littlepinkcloud.invalid> - 2017-02-28 04:02 -0600
Re: Any ideas on faster TLS ? Bernd Paysan <bernd@net2o.de> - 2017-03-11 19:58 +0000
Re: Any ideas on faster TLS ? anton@mips.complang.tuwien.ac.at (Anton Ertl) - 2017-03-01 12:36 +0000
Re: Any ideas on faster TLS ? Ron Aaron <rambamist@gmail.com> - 2017-03-01 15:11 +0200
Re: Any ideas on faster TLS ? Alex <alex@rivadpm.com> - 2017-03-03 01:18 +0000
Re: Any ideas on faster TLS ? anton@mips.complang.tuwien.ac.at (Anton Ertl) - 2017-03-03 12:55 +0000
Re: Any ideas on faster TLS ? Alex <alex@rivadpm.com> - 2017-03-03 14:59 +0000
Re: Any ideas on faster TLS ? anton@mips.complang.tuwien.ac.at (Anton Ertl) - 2017-03-03 16:15 +0000
Optimizing (was: Any ideas on faster TLS ?) Ron Aaron <rambamist@gmail.com> - 2017-03-17 13:14 +0200
Re: Any ideas on faster TLS ? rickman <gnuarm@gmail.com> - 2017-02-28 01:13 -0500
Re: Any ideas on faster TLS ? Paul Rubin <no.email@nospam.invalid> - 2017-02-27 22:48 -0800
Re: Any ideas on faster TLS ? rickman <gnuarm@gmail.com> - 2017-02-28 03:50 -0500
Page 1 of 2 [1] 2 Next page →
| From | Ron Aaron <rambamist@gmail.com> |
|---|---|
| Date | 2017-02-27 07:30 +0200 |
| Subject | Any ideas on faster TLS ? |
| Message-ID | <o90deq$vvp$1@dont-email.me> |
One of the bottlenecks in 8th performance is the fact that the primary stacks as well as a lot of other information are per-thread. Thus, the internal "context" data structure is TLS-based. This works very well: different threads have different stacks and states without conflicting. But since the 'context' is so primary a structure, it gets accessed a lot, and the TLS access (in clang or gcc) is quite slow. In a very old version of 8th compiled with gcc, I used the gcc-specific method of making the 'context' a global, fixed-register variable. That was much, much faster. Unfortunately, clang cannot do that as far as I can tell. I'm using clang instead of gcc, since gcc has a persistent bug (relating to improper alignment of data items; it's an old bug which will probably never get fixed), so the 'fixed-register' idea is out. Does anyone know of a better way to have per-thread data structures rather than just using the "__thread" keyword and relying on the compiler's TLS support? Thanks!
[toc] | [next] | [standalone]
| From | Andrew Haley <andrew29@littlepinkcloud.invalid> |
|---|---|
| Date | 2017-02-27 04:01 -0600 |
| Message-ID | <yuednW2Kf9GZZC7FnZ2dnUU7-KHNnZ2d@supernews.com> |
| In reply to | #55159 |
Ron Aaron <rambamist@gmail.com> wrote:
> One of the bottlenecks in 8th performance is the fact that the
> primary stacks as well as a lot of other information are per-thread.
> Thus, the internal "context" data structure is TLS-based.
>
> This works very well: different threads have different stacks and
> states without conflicting. But since the 'context' is so primary a
> structure, it gets accessed a lot, and the TLS access (in clang or
> gcc) is quite slow.
>
> In a very old version of 8th compiled with gcc, I used the
> gcc-specific method of making the 'context' a global, fixed-register
> variable. That was much, much faster. Unfortunately, clang cannot
> do that as far as I can tell.
>
> I'm using clang instead of gcc, since gcc has a persistent bug
> (relating to improper alignment of data items; it's an old bug which
> will probably never get fixed), so the 'fixed-register' idea is out.
>
> Does anyone know of a better way to have per-thread data structures
> rather than just using the "__thread" keyword and relying on the
> compiler's TLS support?
This is a very baffling post. TLS on x86/Linux generates one
instruction for a read of a __thread variable:
mov %fs:offset,%eax
Position-independent code is a bit worse:
mov %fs:offset,%rax
lea -0x4(%rax),%rax
mov (%rax),%eax
ARM isn't as nice, but still not bad:
mrs x0, tpidr_el0
add x0, x0, #ofs_hi, lsl #12
add x0, x0, #ofs_lo
ldr w0, [x0]
and
adrp x0, <TLS base>
ldr x0, [x0,#offset]
mrs x1, tpidr_el0
ldr w0, [x1,x0]
What OS or architecture are you using that performs poorly?
There are tricks you can do with hashing the stack pointer to an array
index, but these are unlikely to be faster than compiler TLS.
Andrew.
[toc] | [prev] | [next] | [standalone]
| From | Ron Aaron <rambamist@gmail.com> |
|---|---|
| Date | 2017-02-27 13:19 +0200 |
| Message-ID | <o911tb$rfl$1@dont-email.me> |
| In reply to | #55165 |
On 02/27/2017 12:01, Andrew Haley wrote: > This is a very baffling post. TLS on x86/Linux generates one > instruction for a read of a __thread variable: > > mov %fs:offset,%eax Indeed, it's only one instruction. But it's a very expensive one. It's taking up an eighth of the time of a particular function that does a lot more stuff. So maybe there's some trick with aligning segments or something? This is on a 16G Linux/64-bit system (quad CPU)
[toc] | [prev] | [next] | [standalone]
| From | Alex <alex@rivadpm.com> |
|---|---|
| Date | 2017-02-27 12:56 +0000 |
| Message-ID | <o917i9$fl3$1@dont-email.me> |
| In reply to | #55172 |
On 2/27/2017 11:19, Ron Aaron wrote: > > > On 02/27/2017 12:01, Andrew Haley wrote: > >> This is a very baffling post. TLS on x86/Linux generates one >> instruction for a read of a __thread variable: >> >> mov %fs:offset,%eax > > Indeed, it's only one instruction. But it's a very expensive one. It's > taking up an eighth of the time of a particular function that does a lot > more stuff. > > So maybe there's some trick with aligning segments or something? > > This is on a 16G Linux/64-bit system (quad CPU) > On my Windows Forth, I dedicate a register to USER (per thread) class variables. Now FS: only gets referenced for structured exeception handling. It is measurably slower when using MOV reg, FS: offset than occasionally pushing & popping the dedicated reg if it's needed for something else. -- Alex
[toc] | [prev] | [next] | [standalone]
| From | Ron Aaron <rambamist@gmail.com> |
|---|---|
| Date | 2017-02-27 15:10 +0200 |
| Message-ID | <o918bs$ilb$1@dont-email.me> |
| In reply to | #55177 |
On 27/02/2017 14:56, Alex wrote: > On 2/27/2017 11:19, Ron Aaron wrote: >> >> >> On 02/27/2017 12:01, Andrew Haley wrote: >> >>> This is a very baffling post. TLS on x86/Linux generates one >>> instruction for a read of a __thread variable: >>> >>> mov %fs:offset,%eax >> >> Indeed, it's only one instruction. But it's a very expensive one. It's >> taking up an eighth of the time of a particular function that does a lot >> more stuff. >> >> So maybe there's some trick with aligning segments or something? >> >> This is on a 16G Linux/64-bit system (quad CPU) >> > > On my Windows Forth, I dedicate a register to USER (per thread) class > variables. Now FS: only gets referenced for structured exeception > handling. It is measurably slower when using MOV reg, FS: offset than > occasionally pushing & popping the dedicated reg if it's needed for > something else. That's what I did in Reva, but now I'm relying on the C compiler; and clang doesn't allow register (specific register) variables. I suppose I can get around this using a bit of assembler code.
[toc] | [prev] | [next] | [standalone]
| From | albert@cherry.spenarnc.xs4all.nl (Albert van der Horst) |
|---|---|
| Date | 2017-02-27 17:08 +0100 |
| Message-ID | <o91iue$fmr$1@cherry.spenarnc.xs4all.nl> |
| In reply to | #55177 |
In article <o917i9$fl3$1@dont-email.me>, Alex <alex@rivadpm.com> wrote:
>On 2/27/2017 11:19, Ron Aaron wrote:
>>
>>
>> On 02/27/2017 12:01, Andrew Haley wrote:
>>
>>> This is a very baffling post. TLS on x86/Linux generates one
>>> instruction for a read of a __thread variable:
>>>
>>> mov %fs:offset,%eax
>>
>> Indeed, it's only one instruction. But it's a very expensive one. It's
>> taking up an eighth of the time of a particular function that does a lot
>> more stuff.
>>
>> So maybe there's some trick with aligning segments or something?
>>
>> This is on a 16G Linux/64-bit system (quad CPU)
>>
>
>On my Windows Forth, I dedicate a register to USER (per thread) class
>variables. Now FS: only gets referenced for structured exeception
>handling. It is measurably slower when using MOV reg, FS: offset than
>occasionally pushing & popping the dedicated reg if it's needed for
>something else.
An other implementation technique:
The absolute minimum to do cooperative multitasking is switch the
data stack pointer, and the return stack pointer.
Provided the user pointer is derived on the fly from the return stack
pointer each time one uses a user variable. (i86)
ax <- bp \ bp is return stack
ax <- ax or 0x0000FFFF \ example for 64 kbyte return stack
ax <- ax+1
These instructions are very cheap, and are offset by saving one
register on the register starved i86 architecture.
Example: 4 64 bit values on the return stack or 1CBF values
bp: 0x0000,DEAD,BEEF,FFE0 or 0x0000,DEAD,BEEF,1A08
us: 0x0000,DEAD,BEEF,FFFF \ After masking
us: 0x0000,DEAD,BEF0,0000 \ After increment, always same value.
>
>--
>Alex
Groetjes Albert
--
Albert van der Horst, UTRECHT,THE NETHERLANDS
Economic growth -- being exponential -- ultimately falters.
albert@spe&ar&c.xs4all.nl &=n http://home.hccnet.nl/a.w.m.van.der.horst
[toc] | [prev] | [next] | [standalone]
| From | Andrew Haley <andrew29@littlepinkcloud.invalid> |
|---|---|
| Date | 2017-02-27 08:06 -0600 |
| Message-ID | <yPCdnc9TmsTLrynFnZ2dnUU7-TWdnZ2d@supernews.com> |
| In reply to | #55172 |
Ron Aaron <rambamist@gmail.com> wrote: > > > On 02/27/2017 12:01, Andrew Haley wrote: > >> This is a very baffling post. TLS on x86/Linux generates one >> instruction for a read of a __thread variable: >> >> mov %fs:offset,%eax > > Indeed, it's only one instruction. But it's a very expensive one. Really? How expensive? More than a few cycles? > It's taking up an eighth of the time of a particular function that > does a lot more stuff. So maybe there's some trick with aligning > segments or something? No. Why do you keep reloading it? Andrew.
[toc] | [prev] | [next] | [standalone]
| From | Ron Aaron <rambamist@gmail.com> |
|---|---|
| Date | 2017-02-27 16:10 +0200 |
| Message-ID | <o91bst$vj5$1@dont-email.me> |
| In reply to | #55180 |
On 27/02/2017 16:06, Andrew Haley wrote: > Ron Aaron <rambamist@gmail.com> wrote: ... >> Indeed, it's only one instruction. But it's a very expensive one. > > Really? How expensive? More than a few cycles? Yes, as I said: >> It's taking up an eighth of the time of a particular function that >> does a lot more stuff. So maybe there's some trick with aligning >> segments or something?
[toc] | [prev] | [next] | [standalone]
| From | Andrew Haley <andrew29@littlepinkcloud.invalid> |
|---|---|
| Date | 2017-02-27 09:31 -0600 |
| Message-ID | <FeydncNQp7DG2ynFnZ2dnUU7-N-dnZ2d@supernews.com> |
| In reply to | #55181 |
Ron Aaron <rambamist@gmail.com> wrote: > > > On 27/02/2017 16:06, Andrew Haley wrote: >> Ron Aaron <rambamist@gmail.com> wrote: > ... >>> Indeed, it's only one instruction. But it's a very expensive one. >> >> Really? How expensive? More than a few cycles? > > Yes, as I said: > >>> It's taking up an eighth of the time of a particular function that >>> does a lot more stuff. So maybe there's some trick with aligning >>> segments or something? How many cycles is that? That response doesn't help anyone at all. Are you doing it may times, in a loop? How long does each load take? Are we looking at the cost of an L1 cache miss, or something greater? Andrew.
[toc] | [prev] | [next] | [standalone]
| From | Ron Aaron <rambamist@gmail.com> |
|---|---|
| Date | 2017-02-28 07:05 +0200 |
| Message-ID | <o930ae$c3b$1@dont-email.me> |
| In reply to | #55182 |
On 27/02/2017 17:31, Andrew Haley wrote: > Ron Aaron <rambamist@gmail.com> wrote: >>>> It's taking up an eighth of the time of a particular function that >>>> does a lot more stuff. So maybe there's some trick with aligning >>>> segments or something? > > How many cycles is that? That response doesn't help anyone at all. > Are you doing it may times, in a loop? How long does each load take? > Are we looking at the cost of an L1 cache miss, or something greater? I don't know how many cycles it is, but I did do a simple experiment where I simply removed the __thread specifier on the thread-specific context. That resulted in a huge... 5% overall improvement (and obviously wouldn't work for the MT scenario). So even though 'perf' showed the TLS access as being a bottleneck, in practice perhaps the problem is really the memory accesses surrounding the data. Will have to investigate more.
[toc] | [prev] | [next] | [standalone]
| From | Paul Rubin <no.email@nospam.invalid> |
|---|---|
| Date | 2017-02-27 23:15 -0800 |
| Message-ID | <87k28aiy64.fsf@nightsong.com> |
| In reply to | #55182 |
Andrew Haley <andrew29@littlepinkcloud.invalid> writes: > Are we looking at the cost of an L1 cache miss, or something greater? If FS is being reloaded every time, that is quite expensive, or used to be.
[toc] | [prev] | [next] | [standalone]
| From | Andrew Haley <andrew29@littlepinkcloud.invalid> |
|---|---|
| Date | 2017-02-28 04:02 -0600 |
| Message-ID | <gOSdnbJlSYNY1yjFnZ2dnUU7-QPNnZ2d@supernews.com> |
| In reply to | #55235 |
Paul Rubin <no.email@nospam.invalid> wrote: > Andrew Haley <andrew29@littlepinkcloud.invalid> writes: >> Are we looking at the cost of an L1 cache miss, or something greater? > > If FS is being reloaded every time, that is quite expensive, or used to > be. FS is not being reloaded every time. It's loaded at a context switch. I don't think that segment overrides are particularly expensive, so the thread-local load costs little more than any other load. It is, however, in a different region of memory, so it's possible that the load is causing a TLB miss, which would be expensive. But normally TLS accesses are on the order of a few nanoseconds. Andrew.
[toc] | [prev] | [next] | [standalone]
| From | Bernd Paysan <bernd@net2o.de> |
|---|---|
| Date | 2017-03-11 19:58 +0000 |
| Message-ID | <oa1kt5$u80$1@dont-email.me> |
| In reply to | #55249 |
Am Tue, 28 Feb 2017 04:02:45 -0600 schrieb Andrew Haley: > Paul Rubin <no.email@nospam.invalid> wrote: >> Andrew Haley <andrew29@littlepinkcloud.invalid> writes: >>> Are we looking at the cost of an L1 cache miss, or something greater? >> >> If FS is being reloaded every time, that is quite expensive, or used to >> be. > > FS is not being reloaded every time. It's loaded at a context switch. > > I don't think that segment overrides are particularly expensive, so the > thread-local load costs little more than any other load. It is, > however, in a different region of memory, so it's possible that the load > is causing a TLB miss, which would be expensive. But normally TLS > accesses are on the order of a few nanoseconds. When I made Gforth multi-threaded, I first just changed those global variables to __thread variables, and in Linux, I could not measure a significant speed difference. This is especially important for the debugging engine, which saves the IP in every primitive so that exceptions know where the problem happened. Some Windows user then complained that the debugging Gforth engine was incredibly slow on Windows (about a factor 100 slower than before), and it turned out that per-thread on Windows is really expensive. Solution: Have a structure of all those per-thread pointers and let the C compiler figure out to put this structure pointer into a register... The C library wrappers also got changed to pass the stack pointers as arguments and return values, to avoid the expensive per-thread access on Windows. -- Bernd Paysan "If you want it done right, you have to do it yourself" net2o ID: kQusJzA;7*?t=uy@X}1GWr!+0qqp_Cn176t4(dQ* http://bernd-paysan.de/
[toc] | [prev] | [next] | [standalone]
| From | anton@mips.complang.tuwien.ac.at (Anton Ertl) |
|---|---|
| Date | 2017-03-01 12:36 +0000 |
| Message-ID | <2017Mar1.133612@mips.complang.tuwien.ac.at> |
| In reply to | #55172 |
Ron Aaron <rambamist@gmail.com> writes:
>
>
>On 02/27/2017 12:01, Andrew Haley wrote:
>
>> This is a very baffling post. TLS on x86/Linux generates one
>> instruction for a read of a __thread variable:
>>
>> mov %fs:offset,%eax
>
>Indeed, it's only one instruction. But it's a very expensive one.
The best way to find out how expensive is to isolate and measure it,
so I did that:
I compiled the following microbenchmark:
#include <stdio.h>
__thread long a[]={0};
int main()
{
long l=0;
long i;
for (i=0; i<100000000; i++)
l = a[l];
printf("%ld\n",l);
return 0;
}
The inner loop looks as follows:
With __thread: Without __thread:
a: mov %fs:0x0(,%rsi,8),%rsi a: mov 0x0(,%rsi,8),%rsi
13: sub $0x1,%rax 12: sub $0x1,%rax
17: jne a <main+0xa> 16: jne a <main+0xa>
7 cycles/iteration (Haswell) 5 cycles/iteration (Haswell)
4 cycles/iteration (K8) 3 cycles/iteration (K8)
This microbenchmark measures the latency of these loads in the first
line. So the %fs: prefix costs 2 cycles, which is measurable, but not
very expensive. The alternative is using a general-purpose register
for holding the value that %fs holds; the resulting inner-loop code is
1a: mov (%rdx,%rsi,8),%rsi
1e: sub $0x1,%rax
22: jne 1a <main+0x1a>
for this microbenchmark and uses 5 Haswell and 3 K8 cycles/iteration,
i.e., no extra cost. However, in other cases the addition may cost an
extra instruction, and it costs a general-purpose register in any
case.
- anton
--
M. Anton Ertl http://www.complang.tuwien.ac.at/anton/home.html
comp.lang.forth FAQs: http://www.complang.tuwien.ac.at/forth/faq/toc.html
New standard: http://www.forth200x.org/forth200x.html
EuroForth 2016: http://www.euroforth.org/ef16/
[toc] | [prev] | [next] | [standalone]
| From | Ron Aaron <rambamist@gmail.com> |
|---|---|
| Date | 2017-03-01 15:11 +0200 |
| Message-ID | <o96h6k$fc6$1@dont-email.me> |
| In reply to | #55299 |
On 03/01/2017 14:36, Anton Ertl wrote: > Ron Aaron <rambamist@gmail.com> writes: >> Indeed, it's only one instruction. But it's a very expensive one. > > The best way to find out how expensive is to isolate and measure it, > so I did that: Yes; and when I did the rough equivalent, I found it was only a 5% difference so not really the problem.
[toc] | [prev] | [next] | [standalone]
| From | Alex <alex@rivadpm.com> |
|---|---|
| Date | 2017-03-03 01:18 +0000 |
| Message-ID | <o9ag4s$fp2$1@dont-email.me> |
| In reply to | #55300 |
On 3/1/2017 13:11, Ron Aaron wrote:
>
>
> On 03/01/2017 14:36, Anton Ertl wrote:
>> Ron Aaron <rambamist@gmail.com> writes:
>
>>> Indeed, it's only one instruction. But it's a very expensive one.
>>
>> The best way to find out how expensive is to isolate and measure it,
>> so I did that:
>
> Yes; and when I did the rough equivalent, I found it was only a 5%
> difference so not really the problem.
>
The problem is surely the double indirection. For a "standard" variable,
I generate
variable x ok
: foo 10 x ! ; ok
ok
see foo
: foo ( ? -- ? )
\ foo is defined in (console)
\ call compiled
\ code=$41CA5E len=10 type=130
( $0 ) mov dword { $80537C } $A \ C7057C5380000A000000
( $A ) ret \ C3 ( end ) ok
whereas a TLS variable based on a register (eg EBX) (a "user" variable)
generates (the offset of Y up the TLS is $8C)
0 user y ok
: bar 10 y ! ; ok
see bar
: bar ( ? -- ? )
\ bar is defined in (console)
\ call compiled
\ code=$41CAC8 len=10 type=129
( $0 ) mov dword { $8C ebx } $A \ C7838C0000000A000000
( $A ) ret \ C3 ( end ) ok
If the TLS is addressed by FS: then it would be (and this code hand
cranked, because my Forth doesn't generate this);
code tls ok
mov ecx { fs: $e10 } \ first TLS slot in the TEB at $E10 ok
mov { ecx $8c } $a \ offset $8C for the TLS var ok[1]
next; ok
see tls
code tls ( ? -- ? )
\ tls is defined in (console)
\ call compiled
\ code=$41CA4F len=17 type=129
( $0 ) mov ecx dword { fs: $E10 } \ 648B0D100E0000
( $7 ) mov dword { $8C ecx } $A \ C7818C0000000A000000
( $11 ) ret \ C3 ( end ) ok
That's a lot more expensive; every reference is now twice through memory
and with an FS segreg overide on one of the.
--
Alex
[toc] | [prev] | [next] | [standalone]
| From | anton@mips.complang.tuwien.ac.at (Anton Ertl) |
|---|---|
| Date | 2017-03-03 12:55 +0000 |
| Message-ID | <2017Mar3.135535@mips.complang.tuwien.ac.at> |
| In reply to | #55372 |
Alex <alex@rivadpm.com> writes:
>The problem is surely the double indirection. For a "standard" variable,
>I generate
>
>variable x ok
>: foo 10 x ! ; ok
> ok
>see foo
>: foo ( ? -- ? )
>\ foo is defined in (console)
>\ call compiled
>\ code=$41CA5E len=10 type=130
>( $0 ) mov dword { $80537C } $A \ C7057C5380000A000000
>( $A ) ret \ C3 ( end ) ok
>
>whereas a TLS variable based on a register (eg EBX) (a "user" variable)
>generates (the offset of Y up the TLS is $8C)
>
>0 user y ok
>: bar 10 y ! ; ok
>see bar
>: bar ( ? -- ? )
>\ bar is defined in (console)
>\ call compiled
>\ code=$41CAC8 len=10 type=129
>( $0 ) mov dword { $8C ebx } $A \ C7838C0000000A000000
>( $A ) ret \ C3 ( end ) ok
>
>If the TLS is addressed by FS: then it would be (and this code hand
>cranked, because my Forth doesn't generate this);
>
>code tls ok
>mov ecx { fs: $e10 } \ first TLS slot in the TEB at $E10 ok
>mov { ecx $8c } $a \ offset $8C for the TLS var ok[1]
>next; ok
>see tls
>code tls ( ? -- ? )
>\ tls is defined in (console)
>\ call compiled
>\ code=$41CA4F len=17 type=129
>( $0 ) mov ecx dword { fs: $E10 } \ 648B0D100E0000
>( $7 ) mov dword { $8C ecx } $A \ C7818C0000000A000000
>( $11 ) ret \ C3 ( end ) ok
>
>That's a lot more expensive; every reference is now twice through memory
>and with an FS segreg overide on one of the.
It's not clear why you would code it like this. I looked what gcc
generates for
volatile __thread long a;
int main()
{
long l=0;
long i;
for (i=0; i<100000000; i++)
a=10;
return 0;
}
and the inner loop becomes
5: movq $0xa,%fs:0x0
12: sub $0x1,%rax
16: jne 5 <main+0x5>
(the 0x0 is later filled with something sensible by the linker). For
this loop I see 1.00-1.17 cycles/iteration on a Haswell
(1 cycle/iteration is the limit, because the Haswell can only perform
one store per cycle).
Interestingly, without the "__thread" this becomes often slower in 64-bit
mode, with the inner loop looking as follows:
5: movq $0xa,0x0(%rip) # 10 <main+0x10>
10: sub $0x1,%rax
14: jne 5 <main+0x5>
With this I see 1.07-1.43 cycles per iteration.
The large variation in performance for both variants is interesting.
- anton
--
M. Anton Ertl http://www.complang.tuwien.ac.at/anton/home.html
comp.lang.forth FAQs: http://www.complang.tuwien.ac.at/forth/faq/toc.html
New standard: http://www.forth200x.org/forth200x.html
EuroForth 2016: http://www.euroforth.org/ef16/
[toc] | [prev] | [next] | [standalone]
| From | Alex <alex@rivadpm.com> |
|---|---|
| Date | 2017-03-03 14:59 +0000 |
| Message-ID | <o9c08l$jak$1@dont-email.me> |
| In reply to | #55381 |
On 3/3/2017 12:55, Anton Ertl wrote:
> Alex <alex@rivadpm.com> writes:
>> The problem is surely the double indirection. For a "standard" variable,
>> I generate
>>
>> variable x ok
>> : foo 10 x ! ; ok
>> ok
>> see foo
>> : foo ( ? -- ? )
>> \ foo is defined in (console)
>> \ call compiled
>> \ code=$41CA5E len=10 type=130
>> ( $0 ) mov dword { $80537C } $A \ C7057C5380000A000000
>> ( $A ) ret \ C3 ( end ) ok
>>
>> whereas a TLS variable based on a register (eg EBX) (a "user" variable)
>> generates (the offset of Y up the TLS is $8C)
>>
>> 0 user y ok
>> : bar 10 y ! ; ok
>> see bar
>> : bar ( ? -- ? )
>> \ bar is defined in (console)
>> \ call compiled
>> \ code=$41CAC8 len=10 type=129
>> ( $0 ) mov dword { $8C ebx } $A \ C7838C0000000A000000
>> ( $A ) ret \ C3 ( end ) ok
>>
>> If the TLS is addressed by FS: then it would be (and this code hand
>> cranked, because my Forth doesn't generate this);
>>
>> code tls ok
>> mov ecx { fs: $e10 } \ first TLS slot in the TEB at $E10 ok
>> mov { ecx $8c } $a \ offset $8C for the TLS var ok[1]
>> next; ok
>> see tls
>> code tls ( ? -- ? )
>> \ tls is defined in (console)
>> \ call compiled
>> \ code=$41CA4F len=17 type=129
>> ( $0 ) mov ecx dword { fs: $E10 } \ 648B0D100E0000
>> ( $7 ) mov dword { $8C ecx } $A \ C7818C0000000A000000
>> ( $11 ) ret \ C3 ( end ) ok
>>
>> That's a lot more expensive; every reference is now twice through memory
>> and with an FS segreg overide on one of the.
>
> It's not clear why you would code it like this. I looked what gcc
> generates for
>
> volatile __thread long a;
>
> int main()
> {
> long l=0;
> long i;
> for (i=0; i<100000000; i++)
> a=10;
> return 0;
> }
>
> and the inner loop becomes
>
> 5: movq $0xa,%fs:0x0
> 12: sub $0x1,%rax
> 16: jne 5 <main+0x5>
>
> (the 0x0 is later filled with something sensible by the linker). For
> this loop I see 1.00-1.17 cycles/iteration on a Haswell
> (1 cycle/iteration is the limit, because the Haswell can only perform
> one store per cycle).
>
> Interestingly, without the "__thread" this becomes often slower in 64-bit
> mode, with the inner loop looking as follows:
>
> 5: movq $0xa,0x0(%rip) # 10 <main+0x10>
> 10: sub $0x1,%rax
> 14: jne 5 <main+0x5>
>
> With this I see 1.07-1.43 cycles per iteration.
>
> The large variation in performance for both variants is interesting.
>
> - anton
>
Is that on Linux or Windows? FS:0 on Windows points at the TEB (per
thread environment block) which in turn contains the TLS slots. There's
an API call TlsGetValue which gets the slots address initialised by
TlsAlloc, which gives some indication of what we need to go through to
get the TLS data.
A TlsGetValue(slot) call does this:
.imports TlsGetValue
Location Import Prm ImportEP LibName
--------------------------------------------------------------
00805388 TlsGetValue 1 75BF3F90 kernel32.dll
displayed 1 of 70 imported functions ok
$75bf3f90 dis
( $75BF3F90 $75BF3F90 )
( $0 ) mov edi edi \ 8BFF
( $2 ) push ebp \ 55
( $3 ) mov ebp esp \ 8BEC
( $5 ) pop ebp \ 5D
( $6 ) jmp dword { $75C61270 } \ FF257012C675 ok
$75c61270 @ dis
( $75D917D0 $75D917D0 )
( $0 ) mov edi edi \ 8BFF
( $2 ) push ebp \ 55
( $3 ) mov ebp esp \ 8BEC
( $5 ) mov ecx dword { fs: $18 } \ 648B0D18000000
( $C ) mov eax dword { $8 ebp } \ 8B4508
( $F ) mov dword { $34 ecx } $0 \ C7413400000000
( $16 ) cmp eax $40 \ 83F840
( $19 ) jae short $75D917F6 \ 730B
( $1B ) mov eax dword { $E10 ecx eax *4 } \ 8B8481100E0000
( $22 ) pop ebp \ 5D
( $23 ) ret $4 \ C20400 ok
(Line $0 is the two byte patch function that all Windows API calls have.
Line $5 setting ECX is due to the recommendation to access the TEB by
getting the "linear TEB address" at FS:$18 first, which points at FS:$0;
another indirection that appears to be useless cruft. Lines $2, $3, $22
are peculiar, and I don't know why the stack parameters are addressed
like this to support line $C. Line $F sets last error to zero. Lines $16
$19 check for out of range slot counts.)
So, the essence of getting the base address of the TLS area, where EAX
is the slot number:
( $5 ) mov ecx dword { fs: $18 } \ 648B0D18000000
( $1B ) mov eax dword { $E10 ecx eax *4 } \ 8B8481100E0000
That's two indirections already, and we have more to do the store or
fetch of the data from the TLS. But we can (and should, since the
content of FS:$18 is always identical to FS:$0) shorten to this, where
EAX is the slot number:
( $0 ) mov eax dword { fs: $E10 eax *4 } \ 648B0485100E0000
Unless we dedicate a register, there's at least one extra indirection
via FS: on a TLS variable under Windows. That's where I think Ron is
seeing the slowdown.
--
Alex
[toc] | [prev] | [next] | [standalone]
| From | anton@mips.complang.tuwien.ac.at (Anton Ertl) |
|---|---|
| Date | 2017-03-03 16:15 +0000 |
| Message-ID | <2017Mar3.171516@mips.complang.tuwien.ac.at> |
| In reply to | #55382 |
Alex <alex@rivadpm.com> writes:
>On 3/3/2017 12:55, Anton Ertl wrote:
>> volatile __thread long a;
...
>> a=10;
[is compiled to:]
>> 5: movq $0xa,%fs:0x0
...
>Is that on Linux or Windows?
Linux.
- anton
--
M. Anton Ertl http://www.complang.tuwien.ac.at/anton/home.html
comp.lang.forth FAQs: http://www.complang.tuwien.ac.at/forth/faq/toc.html
New standard: http://www.forth200x.org/forth200x.html
EuroForth 2016: http://www.euroforth.org/ef16/
[toc] | [prev] | [next] | [standalone]
| From | Ron Aaron <rambamist@gmail.com> |
|---|---|
| Date | 2017-03-17 13:14 +0200 |
| Subject | Optimizing (was: Any ideas on faster TLS ?) |
| Message-ID | <oagga1$ijq$1@dont-email.me> |
| In reply to | #55300 |
On 01/03/2017 15:11, Ron Aaron wrote: > > > On 03/01/2017 14:36, Anton Ertl wrote: >> Ron Aaron <rambamist@gmail.com> writes: > >>> Indeed, it's only one instruction. But it's a very expensive one. >> >> The best way to find out how expensive is to isolate and measure it, >> so I did that: > > Yes; and when I did the rough equivalent, I found it was only a 5% > difference so not really the problem. One major bottleneck I encountered was the "produce" function to get new items from the pool of available ones. I was using pre-allocated pools of items, which comes at a small cost of startup-time, but a bigger cost of finding an available item. So I changed to a "allocated as needed" pool model, where discarded items are put in a free-list. I wasn't sure if the allocations for new items would make it slower. It made things faster, overall, and helped push another area of code into the bottleneck. Where before, a large math calculation was pinched at the produce stage, now it's getting pinched at the calculation stage, which is much more acceptable. So, once again, making changes according to measured performance is the only way to go...
[toc] | [prev] | [next] | [standalone]
Page 1 of 2 [1] 2 Next page →
Back to top | Article view | comp.lang.forth
csiph-web