(unnamed)

text/plain

Filename: (unnamed)
Type: text/plain
Part: 0
Message: Re: Spinlocks, yet again: analysis and proposed patches
*** src/backend/storage/lmgr/lwlock.c.orig	Sat Aug 20 19:26:24 2005
--- src/backend/storage/lmgr/lwlock.c	Wed Sep 14 11:50:09 2005
***************
*** 27,35 ****
  #include "storage/spin.h"
  
  
  typedef struct LWLock
  {
! 	slock_t		mutex;			/* Protects LWLock and queue of PGPROCs */
  	bool		releaseOK;		/* T if ok to release waiters */
  	char		exclusive;		/* # of exclusive holders (0 or 1) */
  	int			shared;			/* # of shared holders (0..MaxBackends) */
--- 27,44 ----
  #include "storage/spin.h"
  
  
+ /*
+  * Each LWLock has an associated spinlock, which we consider as locking the
+  * content of the LWLock struct as well as the associated queue of waiting
+  * PGPROCs.  To minimize cache-line contention on multiprocessors, the
+  * spinlocks are kept physically separate from their LWLocks --- we don't
+  * want a spinning processor to interfere with access to the LWLock for
+  * the processor holding the spinlock.
+  */
+ 
  typedef struct LWLock
  {
! 	slock_t	   *mutex;			/* Protects LWLock and queue of PGPROCs */
  	bool		releaseOK;		/* T if ok to release waiters */
  	char		exclusive;		/* # of exclusive holders (0 or 1) */
  	int			shared;			/* # of shared holders (0..MaxBackends) */
***************
*** 39,44 ****
--- 48,65 ----
  } LWLock;
  
  /*
+  * We allocate CACHE_LINE_SIZE bytes for the fixed LWLocks' spinlocks.
+  * We assume that the fixed locks are few enough and heavily used enough
+  * that it's worth trying to put each one's spinlock in its own cache line.
+  * The dynamic locks are assumed to be many and less heavily hit, so they
+  * get just MAXALIGN space apiece.  (Packing spinlocks tightly sounds like a
+  * bad idea on machines where slock_t is just a byte ... even for lesser-used
+  * spinlocks, there would be enough locks per cache line to create a
+  * contention issue.)
+  */
+ #define CACHE_LINE_SIZE		64
+ 
+ /*
   * This points to the array of LWLocks in shared memory.  Backends inherit
   * the pointer by fork from the postmaster.  LWLockIds are indexes into
   * the array.
***************
*** 135,144 ****
  	Size		size;
  	int			numLocks = NumLWLocks();
  
! 	/* Allocate the LWLocks plus space for shared allocation counter. */
  	size = mul_size(numLocks, sizeof(LWLock));
  
! 	size = add_size(size, 2 * sizeof(int));
  
  	return size;
  }
--- 156,174 ----
  	Size		size;
  	int			numLocks = NumLWLocks();
  
! 	/* Space for the array of LWLock structs. */
  	size = mul_size(numLocks, sizeof(LWLock));
  
! 	/* Space for dynamic allocation counter and MAXALIGN padding. */
! 	size = add_size(size, 2 * sizeof(int) + MAXIMUM_ALIGNOF);
! 
! 	/* Space for the spinlocks --- see notes for CACHE_LINE_SIZE. */
! 	size = add_size(size,
! 					mul_size((int) NumFixedLWLocks,
! 							 Max(sizeof(slock_t), CACHE_LINE_SIZE)));
! 	size = add_size(size,
! 					mul_size(numLocks - (int) NumFixedLWLocks,
! 							 Max(sizeof(slock_t), MAXIMUM_ALIGNOF)));
  
  	return size;
  }
***************
*** 153,182 ****
  	int			numLocks = NumLWLocks();
  	Size		spaceLocks = LWLockShmemSize();
  	LWLock	   *lock;
  	int			id;
  
  	/* Allocate space */
  	LWLockArray = (LWLock *) ShmemAlloc(spaceLocks);
  
  	/*
  	 * Initialize all LWLocks to "unlocked" state
  	 */
  	for (id = 0, lock = LWLockArray; id < numLocks; id++, lock++)
  	{
! 		SpinLockInit(&lock->mutex);
  		lock->releaseOK = true;
  		lock->exclusive = 0;
  		lock->shared = 0;
  		lock->head = NULL;
  		lock->tail = NULL;
- 	}
  
! 	/*
! 	 * Initialize the dynamic-allocation counter at the end of the array
! 	 */
! 	LWLockCounter = (int *) lock;
! 	LWLockCounter[0] = (int) NumFixedLWLocks;
! 	LWLockCounter[1] = numLocks;
  }
  
  
--- 183,229 ----
  	int			numLocks = NumLWLocks();
  	Size		spaceLocks = LWLockShmemSize();
  	LWLock	   *lock;
+ 	char	   *mutexptr;
  	int			id;
  
  	/* Allocate space */
  	LWLockArray = (LWLock *) ShmemAlloc(spaceLocks);
  
  	/*
+ 	 * Set up dynamic-allocation counter at the end of the array of structs.
+ 	 * (Since struct LWLock contains ints, we should not need any extra
+ 	 * alignment padding here.)
+ 	 */
+ 	LWLockCounter = (int *) (LWLockArray + numLocks);
+ 	LWLockCounter[0] = (int) NumFixedLWLocks;
+ 	LWLockCounter[1] = numLocks;
+ 
+ 	/*
+ 	 * We place the spinlocks after the counter, maxaligning for safety.
+ 	 */
+ 	mutexptr = (char *) (LWLockCounter + 2);
+ 	mutexptr = (char *) MAXALIGN(mutexptr);
+ 
+ 	/*
  	 * Initialize all LWLocks to "unlocked" state
  	 */
  	for (id = 0, lock = LWLockArray; id < numLocks; id++, lock++)
  	{
! 		slock_t	   *mutex = (slock_t *) mutexptr;
! 
! 		SpinLockInit(mutex);
! 		lock->mutex = mutex;
  		lock->releaseOK = true;
  		lock->exclusive = 0;
  		lock->shared = 0;
  		lock->head = NULL;
  		lock->tail = NULL;
  
! 		if (id < (int) NumFixedLWLocks)
! 			mutexptr += Max(sizeof(slock_t), CACHE_LINE_SIZE);
! 		else
! 			mutexptr += Max(sizeof(slock_t), MAXIMUM_ALIGNOF);
! 	}
  }
  
  
***************
*** 207,212 ****
--- 254,260 ----
  LWLockAcquire(LWLockId lockid, LWLockMode mode)
  {
  	volatile LWLock *lock = LWLockArray + lockid;
+ 	volatile slock_t *mutex = lock->mutex;
  	PGPROC	   *proc = MyProc;
  	bool		retry = false;
  	int			extraWaits = 0;
***************
*** 253,259 ****
  		bool		mustwait;
  
  		/* Acquire mutex.  Time spent holding mutex should be short! */
! 		SpinLockAcquire_NoHoldoff(&lock->mutex);
  
  		/* If retrying, allow LWLockRelease to release waiters again */
  		if (retry)
--- 301,307 ----
  		bool		mustwait;
  
  		/* Acquire mutex.  Time spent holding mutex should be short! */
! 		SpinLockAcquire_NoHoldoff(mutex);
  
  		/* If retrying, allow LWLockRelease to release waiters again */
  		if (retry)
***************
*** 304,310 ****
  		lock->tail = proc;
  
  		/* Can release the mutex now */
! 		SpinLockRelease_NoHoldoff(&lock->mutex);
  
  		/*
  		 * Wait until awakened.
--- 352,358 ----
  		lock->tail = proc;
  
  		/* Can release the mutex now */
! 		SpinLockRelease_NoHoldoff(mutex);
  
  		/*
  		 * Wait until awakened.
***************
*** 336,342 ****
  	}
  
  	/* We are done updating shared state of the lock itself. */
! 	SpinLockRelease_NoHoldoff(&lock->mutex);
  
  	/* Add lock to list of locks held by this backend */
  	held_lwlocks[num_held_lwlocks++] = lockid;
--- 384,390 ----
  	}
  
  	/* We are done updating shared state of the lock itself. */
! 	SpinLockRelease_NoHoldoff(mutex);
  
  	/* Add lock to list of locks held by this backend */
  	held_lwlocks[num_held_lwlocks++] = lockid;
***************
*** 359,364 ****
--- 407,413 ----
  LWLockConditionalAcquire(LWLockId lockid, LWLockMode mode)
  {
  	volatile LWLock *lock = LWLockArray + lockid;
+ 	volatile slock_t *mutex = lock->mutex;
  	bool		mustwait;
  
  	PRINT_LWDEBUG("LWLockConditionalAcquire", lockid, lock);
***************
*** 375,381 ****
  	HOLD_INTERRUPTS();
  
  	/* Acquire mutex.  Time spent holding mutex should be short! */
! 	SpinLockAcquire_NoHoldoff(&lock->mutex);
  
  	/* If I can get the lock, do so quickly. */
  	if (mode == LW_EXCLUSIVE)
--- 424,430 ----
  	HOLD_INTERRUPTS();
  
  	/* Acquire mutex.  Time spent holding mutex should be short! */
! 	SpinLockAcquire_NoHoldoff(mutex);
  
  	/* If I can get the lock, do so quickly. */
  	if (mode == LW_EXCLUSIVE)
***************
*** 400,406 ****
  	}
  
  	/* We are done updating shared state of the lock itself. */
! 	SpinLockRelease_NoHoldoff(&lock->mutex);
  
  	if (mustwait)
  	{
--- 449,455 ----
  	}
  
  	/* We are done updating shared state of the lock itself. */
! 	SpinLockRelease_NoHoldoff(mutex);
  
  	if (mustwait)
  	{
***************
*** 424,429 ****
--- 473,479 ----
  LWLockRelease(LWLockId lockid)
  {
  	volatile LWLock *lock = LWLockArray + lockid;
+ 	volatile slock_t *mutex = lock->mutex;
  	PGPROC	   *head;
  	PGPROC	   *proc;
  	int			i;
***************
*** 446,452 ****
  		held_lwlocks[i] = held_lwlocks[i + 1];
  
  	/* Acquire mutex.  Time spent holding mutex should be short! */
! 	SpinLockAcquire_NoHoldoff(&lock->mutex);
  
  	/* Release my hold on lock */
  	if (lock->exclusive > 0)
--- 496,502 ----
  		held_lwlocks[i] = held_lwlocks[i + 1];
  
  	/* Acquire mutex.  Time spent holding mutex should be short! */
! 	SpinLockAcquire_NoHoldoff(mutex);
  
  	/* Release my hold on lock */
  	if (lock->exclusive > 0)
***************
*** 494,500 ****
  	}
  
  	/* We are done updating shared state of the lock itself. */
! 	SpinLockRelease_NoHoldoff(&lock->mutex);
  
  	/*
  	 * Awaken any waiters I removed from the queue.
--- 544,550 ----
  	}
  
  	/* We are done updating shared state of the lock itself. */
! 	SpinLockRelease_NoHoldoff(mutex);
  
  	/*
  	 * Awaken any waiters I removed from the queue.