Groups | Search | Server Info | Keyboard shortcuts | Login | Register [http] [https] [nntp] [nntps]


Groups > comp.lang.forth > #55159 > unrolled thread

Any ideas on faster TLS ?

Started byRon Aaron <rambamist@gmail.com>
First post2017-02-27 07:30 +0200
Last post2017-02-28 03:50 -0500
Articles 20 on this page of 23 — 8 participants

Back to article view | Back to comp.lang.forth


Contents

  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 →


#55159 — Any ideas on faster TLS ?

FromRon Aaron <rambamist@gmail.com>
Date2017-02-27 07:30 +0200
SubjectAny 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]


#55165

FromAndrew Haley <andrew29@littlepinkcloud.invalid>
Date2017-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]


#55172

FromRon Aaron <rambamist@gmail.com>
Date2017-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]


#55177

FromAlex <alex@rivadpm.com>
Date2017-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]


#55178

FromRon Aaron <rambamist@gmail.com>
Date2017-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]


#55183

Fromalbert@cherry.spenarnc.xs4all.nl (Albert van der Horst)
Date2017-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]


#55180

FromAndrew Haley <andrew29@littlepinkcloud.invalid>
Date2017-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]


#55181

FromRon Aaron <rambamist@gmail.com>
Date2017-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]


#55182

FromAndrew Haley <andrew29@littlepinkcloud.invalid>
Date2017-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]


#55225

FromRon Aaron <rambamist@gmail.com>
Date2017-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]


#55235

FromPaul Rubin <no.email@nospam.invalid>
Date2017-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]


#55249

FromAndrew Haley <andrew29@littlepinkcloud.invalid>
Date2017-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]


#55635

FromBernd Paysan <bernd@net2o.de>
Date2017-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]


#55299

Fromanton@mips.complang.tuwien.ac.at (Anton Ertl)
Date2017-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]


#55300

FromRon Aaron <rambamist@gmail.com>
Date2017-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]


#55372

FromAlex <alex@rivadpm.com>
Date2017-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]


#55381

Fromanton@mips.complang.tuwien.ac.at (Anton Ertl)
Date2017-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]


#55382

FromAlex <alex@rivadpm.com>
Date2017-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]


#55384

Fromanton@mips.complang.tuwien.ac.at (Anton Ertl)
Date2017-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]


#55865 — Optimizing (was: Any ideas on faster TLS ?)

FromRon Aaron <rambamist@gmail.com>
Date2017-03-17 13:14 +0200
SubjectOptimizing (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