From: Terje Mathisen on
Tim McCaffrey wrote:
> In article<rrOdncImYb8uBS_XnZ2dnUVZ8tqdnZ2d(a)lyse.net>, Terje.Mathisen(a)tmsw.no
> says...
>>
>> Tim McCaffrey wrote:
>>> Here is one you might find interesting.
>> [snip]
>>> do {
>>> old_head = b->free_list_head;
>>> if (old_head == b->free_list_tail)
>>> return NULL; // list empty
>>> index = b->free_queue[old_head& MAX_BUFFER_CHUNKS];
>>> new_head = _InterlockedCompareExchange(&(b->free_list_head),
>>> old_head+1,
>>> old_head);
>>> }while (new_head != old_head);//compare failed, try again
>>
>> This is yet another O(n*n) (or worse!) algorithm in the face of heavy
>> contention, afaik?
>>
>> Terje
>
> Well, no. The while only executes once if the circular buffer has more than a
> #CPU things in it (for what I developed it for, it did. It was list of free
> buffers).
>
> In heavy use cases (where the circular buffer doesn't go empty), yes,
> everybody is pounding on the head and tail pointers. But at least they aren't
> spinning on them (usually).

I'm worried about the situation where multiple cores are all loading the
list header, then using ICAS to insert the new: Only one of them can win
that exchange, and all the rest must loop, right?

The good thing is that there _will_ be a winner, so you'll see forward
progress.

>
> I made sure the head& tail pointers had their own cache lines.

That's pretty much a given. :-)

Terje

--
- <Terje.Mathisen at tmsw.no>
"almost all programming can be viewed as an exercise in caching"
From: Tim McCaffrey on
In article <Pdidnba4R9Mkqy7XnZ2dnUVZ8h2dnZ2d(a)lyse.net>, Terje.Mathisen(a)tmsw.no
says...
>
>Tim McCaffrey wrote:
>> In article<rrOdncImYb8uBS_XnZ2dnUVZ8tqdnZ2d(a)lyse.net>,
Terje.Mathisen(a)tmsw.no
>> says...
>>>
>>> Tim McCaffrey wrote:
>>>> Here is one you might find interesting.
>>> [snip]
>>>> do {
>>>> old_head = b->free_list_head;
>>>> if (old_head == b->free_list_tail)
>>>> return NULL; // list empty
>>>> index = b->free_queue[old_head& MAX_BUFFER_CHUNKS];
>>>> new_head = _InterlockedCompareExchange(&(b->free_list_head),
>>>> old_head+1,
>>>> old_head);
>>>> }while (new_head != old_head);//compare failed, try again
>>>
>>> This is yet another O(n*n) (or worse!) algorithm in the face of heavy
>>> contention, afaik?
>>>
>>> Terje
>>
>> Well, no. The while only executes once if the circular buffer has more
than a
>> #CPU things in it (for what I developed it for, it did. It was list of
free
>> buffers).
>>
>> In heavy use cases (where the circular buffer doesn't go empty), yes,
>> everybody is pounding on the head and tail pointers. But at least they
aren't
>> spinning on them (usually).
>
>I'm worried about the situation where multiple cores are all loading the
>list header, then using ICAS to insert the new: Only one of them can win
>that exchange, and all the rest must loop, right?
>
>The good thing is that there _will_ be a winner, so you'll see forward
>progress.
>


Yes, your right. For the application I used this in, it almost never
(according to kernrate) had a contention issue there. There was too much
other work going on, so it was unlikely two (or more) threads hit that
problem.

The performance problem I did have was at the CMPXCHG & XADD instructions: I
still had cache thrashing so changing this from a spin-lock protected list to
this "lockless" circular buffer didn't really get me anything.

Live & learn.

- Tim

From: Chris M. Thomasson on
"Tim McCaffrey" <timcaffrey(a)aol.com> wrote in message
news:h90ovp$m9k$1(a)USTR-NEWS.TR.UNISYS.COM...
> In article <Pdidnba4R9Mkqy7XnZ2dnUVZ8h2dnZ2d(a)lyse.net>,
> Terje.Mathisen(a)tmsw.no
> says...
>>
>>Tim McCaffrey wrote:
>>> In article<rrOdncImYb8uBS_XnZ2dnUVZ8tqdnZ2d(a)lyse.net>,
> Terje.Mathisen(a)tmsw.no
>>> says...
>>>>
>>>> Tim McCaffrey wrote:
>>>>> Here is one you might find interesting.
>>>> [snip]
>>>>> do {
>>>>> old_head = b->free_list_head;
>>>>> if (old_head == b->free_list_tail)
>>>>> return NULL; // list empty
>>>>> index = b->free_queue[old_head& MAX_BUFFER_CHUNKS];
>>>>> new_head = _InterlockedCompareExchange(&(b->free_list_head),
>>>>> old_head+1,
>>>>> old_head);
>>>>> }while (new_head != old_head);//compare failed, try again
>>>>
>>>> This is yet another O(n*n) (or worse!) algorithm in the face of heavy
>>>> contention, afaik?
>>>>
>>>> Terje
>>>
>>> Well, no. The while only executes once if the circular buffer has more
> than a
>>> #CPU things in it (for what I developed it for, it did. It was list of
> free
>>> buffers).
>>>
>>> In heavy use cases (where the circular buffer doesn't go empty), yes,
>>> everybody is pounding on the head and tail pointers. But at least they
> aren't
>>> spinning on them (usually).
>>
>>I'm worried about the situation where multiple cores are all loading the
>>list header, then using ICAS to insert the new: Only one of them can win
>>that exchange, and all the rest must loop, right?
>>
>>The good thing is that there _will_ be a winner, so you'll see forward
>>progress.
>>
>
>
> Yes, your right. For the application I used this in, it almost never
> (according to kernrate) had a contention issue there. There was too much
> other work going on, so it was unlikely two (or more) threads hit that
> problem.
>
> The performance problem I did have was at the CMPXCHG & XADD instructions:
> I
> still had cache thrashing so changing this from a spin-lock protected list
> to
> this "lockless" circular buffer didn't really get me anything.
>
> Live & learn.

I agree.


If contention for a particular recourse if few and far between, then a
clever lock-based setup should work out fine.

From: Brett Davis on
In article <5cOdnSMDsdaFmDvXnZ2dnUVZ8qydnZ2d(a)lyse.net>,
Terje Mathisen <Terje.Mathisen(a)tmsw.no> wrote:

> Robert Myers wrote:
> > Prefetch is hugely important, but how it actually works must involve a
> > great deal of reverse-engineering on the part of competitors, because
> > meaningful details never seem to be forthcoming from manufacturers.
> > I'm assuming that Microsoft's compiler designers, for example, know
> > lots of useful things that most others don't, and that they got them
> > from the horse's mouth under an NDA.
>
> I haven't seen a single x86-type CPU, from any manufacturer, where
> letting the compiler issue PREFETCH instructions turns out to be a
> general win.

Let me chime in and say the PREFETCH instructions are useless on MIPS
and PowerPC as well. As currently implemented by hardware they act as
ordinary read instructions, and block one of the two read ports.

The one benefit of PREFETCH is that it will not raise a access violation
when running off the end of an array and into someone else's data, so it
is slightly better than a ordinary read.

Of course when adding PREFETCH slows down your code, that benefit is
academic.

> Yes, they obviously do help a little with SPEC, particularly after the
> compiler has been carefully tuned for these benchmarks, but in real life
> I haven't seen this.
>
> OTOH, hardware-based prefetch, in the form of stream detection in the
> memory interface is indeed a huge win, but the compiler isn't involved
> at all.
From: nmm1 on
In article <qejbb594tjah6s64vff144lickg1m5erat(a)4ax.com>,
Emil Naepflein <netnewsegn(a)kabelmail.de> wrote:
>On Sun, 20 Sep 2009 06:25:09 GMT, Brett Davis <ggtgp(a)yahoo.com> wrote:
>
>>Of course when adding PREFETCH slows down your code, that benefit is
>>academic.
>
>I don't agree here. About 10 years ago I did a lot of performance
>optimizations for TCP checksum and bcopy on R10K cpus. I got performance
>improvements for the this functions of up to 90 %, just by adding PREF
>instructions. In total this reduced cpu consumption per transfered TCP
>byte by about 30 %.

And the date's the point. Up until recently, the main CPU bottleneck
in most programs was memory latency, and only a small proportion of
the available bandwidth was actually used - except, of course, in
tuned HPC programs (cue John McCalpin). But, with the increasing
number of cores, bandwidth is returning as the main bottleneck.

Prefetching uses extra bandwidth to save on latency, and thus works
only when you have a surplus of the former and the latter is the
problem.


Regards,
Nick Maclaren.