From: Chuck Ebbert <76306.1226@compuserve.com>
To: linux-kernel <linux-kernel@vger.kernel.org>
Cc: linux@horizon.com, Linus Torvalds <torvalds@osdl.org>,
Kirill Korotaev <dev@sw.ru>
Subject: i386 spinlock fairness: bizarre test results
Date: Tue, 11 Oct 2005 00:04:52 -0400 [thread overview]
Message-ID: <200510110007_MC3-1-AC4C-97EA@compuserve.com> (raw)
After seeing Kirill's message about spinlocks I decided to do my own
testing with the userspace program below; the results were very strange.
When using the 'mov' instruction to do the unlock I was able to reproduce
hogging of the spinlock by a single CPU even on Pentium II under some
conditions, while using 'xchg' always allowed the other CPU to get the
lock:
[me@d2 t]$ gcc -O2 -o spintest.o -Wall -DUSE_MOV=1 spintest.c ; ./spintest.o
parent CPU 1, child CPU 0, using mov instruction for unlock
parent did: 34534 of 10000001 iterations
CPU clocks: 2063591864
[me@d2 t]$ gcc -O2 -o spintest.o -Wall -DUSE_MOV=0 spintest.c ; ./spintest.o
parent CPU 1, child CPU 0, using xchg instruction for unlock
parent did: 5169760 of 10000001 iterations
CPU clocks: 2164689784
The results were dependent on the alignment of the "lock ; decl" at the start
of the spinlock code. If that 7-byte instruction spanned two cachelines then
the CPU with that code could somehow hog the spinlock. Optimizing for
Pentium II forced 16-byte alignment and made the spinlock fairer, but still
somewhat biased when using 'mov':
[me@d2 t]$ gcc -O2 -o spintest.o -Wall -DUSE_MOV=1 -mcpu=pentium2 spintest.c ; ./spintest.o
parent CPU 1, child CPU 0, using mov instruction for unlock
parent did: 4181147 of 10000001 iterations
CPU clocks: 2057436825
That test machine was a dual 350MHz Pentium II Xeon; on a dual 333MHz Pentium II
Overdrive (with very slow Socket 8 bus) I could not reproduce those results.
However, on that machine the 'xchg' instruction made the test run almost 20%
_faster_ than using 'mov'.
So I think the i386 spinlock code should be changed to always use 'xchg' to do
spin_unlock.
================================================================================
/* spinlock test
*
* Tests spinlock fairness on SMP i386 machine.
*/
/* number of tests */
#ifndef ITERS
#define ITERS 10000000
#endif
/* use "mov" instruction for spin_unlock instead of "xchg" */
#ifndef USE_MOV
#define USE_MOV 1
#endif
/* cpu to run parent process; child will use !PARENT_CPU */
#ifndef PARENT_CPU
#define PARENT_CPU 1
#endif
/* change this to match your version of glibc -- "2" means use 2 args */
#ifndef SETAFFINITY
#define SETAFFINITY 2
#endif
#if SETAFFINITY == 2
#define setaffinity(pid, mask) sched_setaffinity((pid), &(mask))
#else /* 3 args */
#define setaffinity(pid, mask) sched_setaffinity((pid), sizeof(mask), &(mask))
#endif
#define _GNU_SOURCE
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <fcntl.h>
#include <sched.h>
#include <sys/ptrace.h>
#include <sys/wait.h>
#include <sys/mman.h>
#include <asm/user.h>
#define likely(x) __builtin_expect(!!(x), 1)
#define unlikely(x) __builtin_expect(!!(x), 0)
#define RDTSCLL(r) asm volatile("rdtsc" : "=A" (r))
unsigned long long tsc1, tsc2;
cpu_set_t cpuset0, cpuset1;
int test = 1; /* for testing -- starts unlocked */
int strt = 0; /* sync startup -- starts locked */
int stk[1024];
int iters, p_iters, c_iters;
static inline void __raw_spin_lock(int *l)
{
asm volatile(
"\n1:\t"
"lock ; decl %0\n\t"
"jns 3f\n"
"2:\t"
"rep;nop\n\t"
"cmpl $0,%0\n\t"
"jle 2b\n\t"
"jmp 1b\n"
"3:\n\t"
:"=m" (*l) : : "memory");
}
#if USE_MOV
static inline void __raw_spin_unlock(int *l)
{
asm volatile("movl $1,%0"
: "=m" (*l) : : "memory");
}
#else
static inline void __raw_spin_unlock(int *l)
{
int oldval = 1;
asm volatile("xchgl %0,%1"
: "=q" (oldval), "=m" (*l)
: "0" (oldval) : "memory");
}
#endif /* USE_MOV */
int do_child(void *vp)
{
CPU_SET(!PARENT_CPU, &cpuset1);
if (unlikely(setaffinity(0, cpuset1)))
perror("child setaffinity");
/* Add "nop" insns as necessary to make 1st
* insn of __raw_spin_lock span cachelines.
*/
asm("nop ; nop ; nop");
__raw_spin_unlock(&strt); /* release parent */
again:
__raw_spin_lock(&test);
c_iters++;
if (likely(++iters < ITERS)) {
__raw_spin_unlock(&test);
goto again;
}
__raw_spin_unlock(&test);
return 0;
}
int main(int argc, char * const argv[])
{
CPU_SET(PARENT_CPU, &cpuset0);
if (unlikely(setaffinity(0, cpuset0)))
perror("parent setaffinity");
clone(do_child, &stk[1023], CLONE_VM, 0);
__raw_spin_lock(&strt); /* wait for child to init */
RDTSCLL(tsc1);
again:
__raw_spin_lock(&test);
p_iters++;
if (likely(++iters < ITERS)) {
__raw_spin_unlock(&test);
goto again;
}
__raw_spin_unlock(&test);
RDTSCLL(tsc2);
printf("parent CPU %d, child CPU %d, ", PARENT_CPU, !PARENT_CPU);
printf("using %s instruction for unlock\n", USE_MOV ? "mov" : "xchg");
printf("parent did: %d of %d iterations\n", p_iters, iters);
printf("CPU clocks: %14llu\n", tsc2 - tsc1);
return 0;
}
__
Chuck
next reply other threads:[~2005-10-11 4:08 UTC|newest]
Thread overview: 25+ messages / expand[flat|nested] mbox.gz Atom feed top
2005-10-11 4:04 Chuck Ebbert [this message]
2005-10-11 9:42 ` Eric Dumazet
2005-10-11 13:00 ` Alan Cox
2005-10-11 14:44 ` Linus Torvalds
2005-10-11 15:32 ` [PATCH] i386 spinlocks should use the full 32 bits, not only 8 bits Eric Dumazet
2005-10-11 16:03 ` Linus Torvalds
2005-10-11 16:36 ` Eric Dumazet
2005-10-11 16:54 ` Linus Torvalds
2005-10-11 16:55 ` Linus Torvalds
2005-10-17 7:03 ` Andrew Morton
2005-10-17 7:20 ` Arjan van de Ven
2005-10-20 21:50 ` Ingo Molnar
2005-10-20 21:57 ` Linus Torvalds
2005-10-20 22:02 ` Ingo Molnar
2005-10-20 22:15 ` Linus Torvalds
2005-10-20 22:27 ` Ingo Molnar
2005-10-20 22:44 ` Andrew Morton
2005-10-20 22:53 ` Ingo Molnar
2005-10-20 23:01 ` Andrew Morton
2005-10-20 23:26 ` Ingo Molnar
2005-10-27 16:54 ` Paul Jackson
2005-10-11 17:59 ` Andi Kleen
[not found] <4WjCM-7Aq-7@gated-at.bofh.it>
2005-10-11 4:32 ` i386 spinlock fairness: bizarre test results Robert Hancock
2005-10-11 12:31 ` Joe Seigh
2005-10-11 21:01 ` Bill Davidsen
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=200510110007_MC3-1-AC4C-97EA@compuserve.com \
--to=76306.1226@compuserve.com \
--cc=dev@sw.ru \
--cc=linux-kernel@vger.kernel.org \
--cc=linux@horizon.com \
--cc=torvalds@osdl.org \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox
all inboxes | Powered by JetHome®