Here is the long awaited optimized version of the S_LOCK patch.

This incorporates all the precedeing patches and emailed suggestions
and the results of the performance testing I posted last week. I
would like to get this tested on as many platforms as possible so
I can verify it went in correctly (as opposed to the horrorshow
last time I sent in a patch).

Once this is confirmed, I will make a tarball of files that can be
dropped into a 6.3.2 source tree as a few people have asked for
this in 6.3.2 as well.

David Gould
This commit is contained in:
Bruce Momjian 1998-06-16 07:18:16 +00:00
parent 7784312f99
commit 0d8e7f6381
3 changed files with 199 additions and 256 deletions

View File

@ -4,7 +4,7 @@
# Makefile for storage/buffer
#
# IDENTIFICATION
# $Header: /cvsroot/pgsql/src/backend/storage/buffer/Makefile,v 1.10 1998/05/04 16:58:37 scrappy Exp $
# $Header: /cvsroot/pgsql/src/backend/storage/buffer/Makefile,v 1.11 1998/06/16 07:18:15 momjian Exp $
#
#-------------------------------------------------------------------------
@ -27,7 +27,7 @@ clean:
rm -f SUBSYS.o $(OBJS) s_lock_test
s_lock_test: s_lock.c
$(CC) $(CFLAGS) -DS_LOCK_TEST=1 -g s_lock.c -o s_lock_test
$(CC) $(CFLAGS) -DS_LOCK_TEST=1 s_lock.c -o s_lock_test
./s_lock_test
ifeq (depend,$(wildcard depend))

View File

@ -7,12 +7,13 @@
*
*
* IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/storage/buffer/Attic/s_lock.c,v 1.7 1998/05/04 23:49:09 scrappy Exp $
* $Header: /cvsroot/pgsql/src/backend/storage/buffer/Attic/s_lock.c,v 1.8 1998/06/16 07:18:15 momjian Exp $
*
*-------------------------------------------------------------------------
*/
#include <stdio.h>
#include <sys/time.h>
#include "config.h"
#include "c.h"
@ -22,50 +23,24 @@
/*
* Each time we busy spin we select the next element of this array as the
* number of microseconds to wait. This accomplishes pseudo random back-off.
* Values are not critical and are weighted to the low end of the range. They
* were chosen to work even with different select() timer resolutions on
* different platforms.
* note: total time to cycle through all 16 entries might be about .1 second.
* Values are not critical but 10 milliseconds is a common platform
* granularity.
* note: total time to cycle through all 16 entries might be about .07 sec.
*/
int s_spincycle[S_NSPINCYCLE] =
{0, 0, 0, 1000, 5000, 0, 10000, 3000,
0, 10000, 0, 15000, 9000, 21000, 6000, 30000
#define S_NSPINCYCLE 20
#define S_MAX_BUSY 500 * S_NSPINCYCLE
int s_spincycle[S_NSPINCYCLE] =
{ 0, 0, 0, 0, 10000, 0, 0, 0, 10000, 0,
0, 10000, 0, 0, 10000, 0, 10000, 0, 10000, 10000
};
#if defined(S_LOCK_DEBUG)
/*
* s_lock(lock) - take a spinlock
* add intrumentation code to this and define S_LOCK_DEBUG
* instead of hacking up the macro in s_lock.h
* s_lock_stuck(lock) - complain about a stuck spinlock
*/
void
s_lock(slock_t *lock, char *file, int line)
{
int spins = 0;
while (TAS(lock))
{
struct timeval delay;
delay.tv_sec = 0;
delay.tv_usec = s_spincycle[spins++ % S_NSPINCYCLE];
(void) select(0, NULL, NULL, NULL, &delay);
if (spins > S_MAX_BUSY)
{
/* It's been well over a minute... */
s_lock_stuck(lock, file, line);
}
}
}
#endif /* S_LOCK_DEBUG */
/*
* s_lock_stuck(lock) - deal with stuck spinlock
*/
void
s_lock_stuck(slock_t *lock, char *file, int line)
static void
s_lock_stuck(volatile slock_t *lock, const char *file, const int line)
{
fprintf(stderr,
"\nFATAL: s_lock(%08x) at %s:%d, stuck spinlock. Aborting.\n",
@ -79,84 +54,51 @@ s_lock_stuck(slock_t *lock, char *file, int line)
/*
* Various TAS implementations moved from s_lock.h to avoid redundant
* definitions of the same routine.
* RESOLVE: move this to tas.c. Alternatively get rid of tas.[cso] and fold
* all that into this file.
* s_lock(lock) - take a spinlock with backoff
*/
void
s_lock(volatile slock_t *lock, const char *file, const int line)
{
int spins = 0;
while (TAS(lock))
{
struct timeval delay;
delay.tv_sec = 0;
delay.tv_usec = s_spincycle[spins % S_NSPINCYCLE];
(void) select(0, NULL, NULL, NULL, &delay);
if (++spins > S_MAX_BUSY)
{
/* It's been over a minute... */
s_lock_stuck(lock, file, line);
}
}
}
/*
* Various TAS implementations that cannot live in s_lock.h as no inline
* definition exists (yet).
* In the future, get rid of tas.[cso] and fold it into this file.
*/
#if defined(linux)
#if defined(__GNUC__)
/*************************************************************************
* All the Linux flavors
* All the gcc flavors that are not inlined
*/
#if defined(__alpha)
int
tas(slock_t *lock)
{
slock_t _res;
__asm__(" ldq $0, %0 \n\
bne $0, already_set \n\
ldq_l $0, %0 \n\
bne $0, already_set \n\
or $31, 1, $0 \n\
stq_c $0, %0 \n\
beq $0, stqc_fail \n\
success: bis $31, $31, %1 \n\
mb \n\
jmp $31, end \n\
stqc_fail: or $31, 1, $0 \n\
already_set: bis $0, $0, %1 \n\
end: nop ": "=m"(*lock), "=r"(_res): :"0");
return (_res != 0);
}
#endif /* __alpha */
#if defined(i386)
int
tas(slock_t *lock)
{
slock_t _res = 1;
__asm__("lock; xchgb %0,%1": "=q"(_res), "=m"(*lock):"0"(0x1));
return (_res != 0);
}
#endif /* i386 */
#if defined(sparc)
int
tas(slock_t *lock)
{
slock_t _res;
slock_t *tmplock = lock;
__asm__("ldstub [%1], %0" \
: "=&r"(_res), "=r"(tmplock) \
: "1"(tmplock));
return (_res != 0);
}
#endif /* sparc */
#if defined(PPC)
static int
tas_dummy()
/* Note: need a nice gcc constrained asm version so it can be inlined */
int
tas(volatile slock_t *lock)
{
__asm__(" \n\
tas: \n\
lwarx 5,0,3 \n\
__asm__("lwarx 5,0,3 \n\
cmpwi 5,0 \n\
bne fail \n\
addi 5,5,1 \n\
@ -169,21 +111,20 @@ success: \n\
blr \n\
");
}
#endif /* PPC */
#else /* defined(linux) */
#else /* defined(__GNUC__) */
/***************************************************************************
* All Non-Linux
* All non gcc
*/
#if defined(sun3)
static void
tas_dummy() /* really means: extern int tas(slock_t *lock); */
tas_dummy() /* really means: extern int tas(slock_t *lock); */
{
asm("LLA0:");
asm(" .data");
@ -208,26 +149,18 @@ tas_dummy() /* really means: extern int tas(slock_t *lock); */
#if defined(NEED_SPARC_TAS_ASM)
/*
* bsd and bsdi sparc machines
* sparc machines not using gcc
*/
/* if we're using -ansi w/ gcc, use __asm__ instead of asm */
#if defined(__STRICT_ANSI__)
#define asm(x) __asm__(x)
#endif /* __STRICT_ANSI__ */
static void
tas_dummy() /* really means: extern int tas(slock_t *lock); */
tas_dummy() /* really means: extern int tas(slock_t *lock); */
{
asm(".seg \"data\"");
asm(".seg \"text\"");
asm("_tas:");
/*
* Sparc atomic test and set (sparc calls it "atomic load-store")
*/
asm("ldstub [%r8], %r8");
asm("retl");
asm("nop");
}
@ -237,76 +170,25 @@ tas_dummy() /* really means: extern int tas(slock_t *lock); */
#if defined(NEED_VAX_TAS_ASM)
/*
* VAXen -- even multiprocessor ones
* (thanks to Tom Ivar Helbekkmo)
*/
typedef unsigned char slock_t;
int
tas(slock_t *lock)
{
register ret;
asm(" movl $1, r0
bbssi $0, (%1), 1f
clrl r0
1: movl r0, %0 "
: "=r"(ret) /* return value, in register */
: "r"(lock) /* argument, 'lock pointer', in register */
: "r0"); /* inline code uses this register */
return ret;
}
#endif /* NEED_VAX_TAS_ASM */
#if defined(NEED_I386_TAS_ASM)
/*
* i386 based things
*/
#if defined(USE_UNIVEL_CC)
asm int
tas(slock_t *s_lock)
{
%lab locked;
/* Upon entry, %eax will contain the pointer to the lock byte */
pushl % ebx
xchgl % eax, %ebx
xor % eax, %eax
movb $255, %al
lock
xchgb % al, (%ebx)
popl % ebx
}
#else /* USE_UNIVEL_CC */
int
tas(slock_t *lock)
{
slock_t _res = 1;
__asm__("lock; xchgb %0,%1": "=q"(_res), "=m"(*lock):"0"(0x1));
return (_res != 0);
}
#endif /* USE_UNIVEL_CC */
/* non gcc i386 based things */
#endif /* NEED_I386_TAS_ASM */
#endif /* linux */
#endif /* not __GNUC__ */
/*****************************************************************************/
#if defined(S_LOCK_TEST)
slock_t test_lock;
/*
* test program for verifying a port.
*/
volatile slock_t test_lock;
void
main()
@ -330,7 +212,7 @@ main()
printf("S_LOCK_TEST: this will hang for a few minutes and then abort\n");
printf(" with a 'stuck spinlock' message if S_LOCK()\n");
printf(" and TAS() are working.\n");
S_LOCK(&test_lock);
s_lock(&test_lock, __FILE__, __LINE__);
printf("S_LOCK_TEST: failed, lock not locked~\n");
exit(3);
@ -338,3 +220,4 @@ main()
}
#endif /* S_LOCK_TEST */

View File

@ -7,13 +7,14 @@
*
*
* IDENTIFICATION
* $Header: /cvsroot/pgsql/src/include/storage/s_lock.h,v 1.35 1998/06/15 18:40:04 momjian Exp $
* $Header: /cvsroot/pgsql/src/include/storage/s_lock.h,v 1.36 1998/06/16 07:18:16 momjian Exp $
*
*-------------------------------------------------------------------------
*/
/*
* DESCRIPTION
* The public functions that must be provided are:
* The public macros that must be provided are:
*
* void S_INIT_LOCK(slock_t *lock)
*
@ -45,11 +46,15 @@
* #define TAS(lock) tas(lock)
* int tas(slock_t *lock) // True if lock already set
*
* There are default implementations for all these macros at the bottom
* of this file. Check if your platform can use these or needs to
* override them.
*
* NOTES
* If none of this can be done, POSTGRES will default to using
* System V semaphores (and take a large performance hit -- around 40%
* of its time on a DS5000/240 is spent in semop(3)...).
*
* NOTES
* AIX has a test-and-set but the recommended interface is the cs(3)
* system call. This provides an 8-instruction (plus system call
* overhead) uninterruptible compare-and-set operation. True
@ -57,10 +62,6 @@
* regression test suite by about 25%. I don't have an assembler
* manual for POWER in any case.
*
* There are default implementations for all these macros at the bottom
* of this file. Check if your platform can use these or needs to
* override them.
*
*/
#if !defined(S_LOCK_H)
#define S_LOCK_H
@ -69,23 +70,104 @@
#if defined(HAS_TEST_AND_SET)
#if defined(linux)
/***************************************************************************
* All Linux
#if defined(__GNUC__)
/*************************************************************************
* All the gcc inlines
*/
#if defined(__alpha)
#define TAS(lock) tas(lock)
#define S_UNLOCK(lock) { __asm__("mb"); *(lock) = 0; }
static __inline__ int
tas(volatile slock_t *lock)
{
register slock_t _res;
__asm__(" ldq $0, %0 \n\
bne $0, already_set \n\
ldq_l $0, %0 \n\
bne $0, already_set \n\
or $31, 1, $0 \n\
stq_c $0, %0 \n\
beq $0, stqc_fail \n\
success: bis $31, $31, %1 \n\
mb \n\
jmp $31, end \n\
stqc_fail: or $31, 1, $0 \n\
already_set: bis $0, $0, %1 \n\
end: nop " : "=m"(*lock), "=r"(_res) : : "0");
return (int) _res;
}
#endif /* __alpha */
#if defined(i386)
#define TAS(lock) tas(lock)
#else /* linux */
static __inline__ int
tas(volatile slock_t *lock)
{
register slock_t _res = 1;
__asm__("lock; xchgb %0,%1" : "=q"(_res), "=m"(*lock) : "0"(_res) );
return (int) _res;
}
#endif /* i386 */
#if defined(sparc)
#define TAS(lock) tas(lock)
static __inline__ int
tas(volatile slock_t *lock)
{
register slock_t _res = 1;
__asm__("ldstub [%1], %0" \
: "=r"(_res), "=m"(*lock) \
: "1"(lock));
return (int) _res;
}
#endif /* sparc */
#if defined(NEED_VAX_TAS_ASM)
/*
* VAXen -- even multiprocessor ones
* (thanks to Tom Ivar Helbekkmo)
*/
#define TAS(lock) tas(lock)
typedef unsigned char slock_t;
static __inline__ int
tas(volatile slock_t *lock)
{
register _res;
__asm__(" movl $1, r0
bbssi $0, (%1), 1f
clrl r0
1: movl r0, %0 "
: "=r"(_res) /* return value, in register */
: "r"(lock) /* argument, 'lock pointer', in register */
: "r0"); /* inline code uses this register */
return (int) _res;
}
#endif /* NEED_VAX_TAS_ASM */
#else /* __GNUC__ */
/***************************************************************************
* All non Linux
* All non gcc
*/
#if defined (nextstep)
@ -95,14 +177,10 @@
*/
#define S_LOCK(lock) mutex_lock(lock)
#define S_UNLOCK(lock) mutex_unlock(lock)
#define S_INIT_LOCK(lock) mutex_init(lock)
/* For Mach, we have to delve inside the entrails of `struct mutex'. Ick! */
#define S_LOCK_FREE(alock) ((alock)->lock == 0)
#endif /* nextstep */
@ -118,13 +196,9 @@
* for the R3000 chips out there.
*/
#define TAS(lock) (!acquire_lock(lock))
#define S_UNLOCK(lock) release_lock(lock)
#define S_INIT_LOCK(lock) init_lock(lock)
#define S_LOCK_FREE(lock) (stat_lock(lock) == UNLOCKED)
#endif /* __sgi */
@ -137,13 +211,9 @@
* (see storage/ipc.h).
*/
#define TAS(lock) (msem_lock((lock), MSEM_IF_NOWAIT) < 0)
#define S_UNLOCK(lock) msem_unlock((lock), 0)
#define S_INIT_LOCK(lock) msem_init((lock), MSEM_UNLOCKED)
#define S_LOCK_FREE(lock) (!(lock)->msem_state)
#endif /* __alpha */
@ -156,7 +226,6 @@
* (see storage/ipc.h).
*/
#define TAS(lock) cs((int *) (lock), 0, 1)
#endif /* _AIX */
@ -175,14 +244,38 @@ static slock_t clear_lock =
{-1, -1, -1, -1};
#define S_UNLOCK(lock) (*(lock) = clear_lock) /* struct assignment */
#define S_LOCK_FREE(lock) ( *(int *) (((long) (lock) + 15) & ~15) != 0)
#endif /* __hpux */
#endif /* else defined(linux) */
#if defined(NEED_I386_TAS_ASM)
/* non gcc i386 based things */
#if defined(USE_UNIVEL_CC)
#define TAS(lock) tas(lock)
asm int
tas(slock_t *s_lock)
{
%lab locked;
/* Upon entry, %eax will contain the pointer to the lock byte */
pushl % ebx
xchgl % eax, %ebx
xor % eax, %eax
movb $255, %al
lock
xchgb % al, (%ebx)
popl % ebx
}
#endif /* USE_UNIVEL_CC */
#endif /* NEED_I386_TAS_ASM */
#endif /* else defined(__GNUC__) */
@ -192,47 +285,15 @@ static slock_t clear_lock =
*/
#if !defined(S_LOCK)
#include <sys/time.h>
#define S_NSPINCYCLE 16
#define S_MAX_BUSY 1000 * S_NSPINCYCLE
extern int s_spincycle[];
extern void s_lock_stuck(slock_t *lock, char *file, int line);
#if defined(S_LOCK_DEBUG)
extern void s_lock(slock_t *lock);
#define S_LOCK(lock) s_lock(lock, __FILE__, __LINE__)
#else /* S_LOCK_DEBUG */
extern void s_lock(volatile slock_t *lock, const char *file, const int line);
#define S_LOCK(lock) \
do { \
int spins = 0; \
while (TAS(lock)) \
{ \
struct timeval delay; \
delay.tv_sec = 0; \
delay.tv_usec = s_spincycle[spins++ % S_NSPINCYCLE]; \
(void) select(0, NULL, NULL, NULL, &delay); \
if (spins > S_MAX_BUSY) \
{ \
/* It's been well over a minute... */ \
s_lock_stuck(lock, __FILE__, __LINE__); \
} \
} \
} while(0)
#endif /* S_LOCK_DEBUG */
if (TAS((volatile slock_t *) lock)) {\
s_lock((volatile slock_t *) lock, __FILE__, __LINE__); \
} else
#endif /* S_LOCK */
#if !defined(S_LOCK_FREE)
#define S_LOCK_FREE(lock) ((*lock) == 0)
#define S_LOCK_FREE(lock) (*(lock) == 0)
#endif /* S_LOCK_FREE */
#if !defined(S_UNLOCK)
@ -244,12 +305,11 @@ do { \
#endif /* S_INIT_LOCK */
#if !defined(TAS)
int tas(slock_t *lock); /* port/.../tas.s, or s_lock.c */
#define TAS(lock) tas(lock)
int tas(volatile slock_t *lock); /* port/.../tas.s, or s_lock.c */
#define TAS(lock) tas((volatile slock_t *) lock)
#endif /* TAS */
#endif /* HAS_TEST_AND_SET */
#endif /* S_LOCK_H */