Improve spinlock performance for HP-UX, ia64, non-gcc.
authorRobert Haas <rhaas@postgresql.org>
Mon, 29 Aug 2011 14:05:48 +0000 (10:05 -0400)
committerRobert Haas <rhaas@postgresql.org>
Mon, 29 Aug 2011 14:05:48 +0000 (10:05 -0400)
At least on this architecture, it's very important to spin on a
non-atomic instruction and only retry the atomic once it appears
that it will succeed.  To fix this, split TAS() into two macros:
TAS(), for trying to grab the lock the first time, and TAS_SPIN(),
for spinning until we get it.  TAS_SPIN() defaults to same as TAS(),
but we can override it when we know there's a better way.

It's likely that some of the other cases in s_lock.h require
similar treatment, but this is the only one we've got conclusive
evidence for at present.

src/backend/storage/lmgr/s_lock.c
src/include/storage/s_lock.h

index 1b678075a8040d8e33cfa9bd9cd36b51c0776cdb..1aa9912572e2b27416b48fedc04175a10ea46758 100644 (file)
@@ -96,7 +96,7 @@ s_lock(volatile slock_t *lock, const char *file, int line)
    int         delays = 0;
    int         cur_delay = 0;
 
-   while (TAS(lock))
+   while (TAS_SPIN(lock))
    {
        /* CPU-specific delay each time through the loop */
        SPIN_DELAY();
index 7fe01aac616cdc2a34c2d062f46c290a67cc973f..ac4549d7bb94f7b5a5be950126990d2215550064 100644 (file)
  * macros at the bottom of the file.  Check if your platform can use
  * these or needs to override them.
  *
- *  Usually, S_LOCK() is implemented in terms of an even lower-level macro
- * TAS():
+ *  Usually, S_LOCK() is implemented in terms of even lower-level macros
+ * TAS() and TAS_SPIN():
  *
  * int TAS(slock_t *lock)
  *     Atomic test-and-set instruction.  Attempt to acquire the lock,
  *     but do *not* wait.  Returns 0 if successful, nonzero if unable
  *     to acquire the lock.
  *
- * TAS() is NOT part of the API, and should never be called directly.
+ * int TAS_SPIN(slock_t *lock)
+ *     Like TAS(), but this version is used when waiting for a lock
+ *     previously found to be contended.  Typically, this is the
+ *     same as TAS(), but on some architectures it's better to poll a
+ *     contended lock using an unlocked instruction and retry the
+ *     atomic test-and-set only when it appears free.
  *
- * CAUTION: on some platforms TAS() may sometimes report failure to acquire
- * a lock even when the lock is not locked.  For example, on Alpha TAS()
- * will "fail" if interrupted.  Therefore TAS() should always be invoked
- * in a retry loop, even if you are certain the lock is free.
+ * TAS() and TAS_SPIN() are NOT part of the API, and should never be called
+ * directly.
  *
- * ANOTHER CAUTION: be sure that TAS() and S_UNLOCK() represent sequence
- * points, ie, loads and stores of other values must not be moved across
- * a lock or unlock.  In most cases it suffices to make the operation be
- * done through a "volatile" pointer.
+ * CAUTION: on some platforms TAS() and/or TAS_SPIN() may sometimes report
+ * failure to acquire a lock even when the lock is not locked.  For example,
+ * on Alpha TAS() will "fail" if interrupted.  Therefore a retry loop must
+ * always be used, even if you are certain the lock is free.
+ *
+ * ANOTHER CAUTION: be sure that TAS(), TAS_SPIN(), and S_UNLOCK() represent
+ * sequence points, ie, loads and stores of other values must not be moved
+ * across a lock or unlock.  In most cases it suffices to make the operation
+ * be done through a "volatile" pointer.
  *
  * On most supported platforms, TAS() uses a tas() function written
  * in assembly language to execute a hardware atomic-test-and-set
@@ -727,6 +735,7 @@ typedef unsigned int slock_t;
 
 #include <ia64/sys/inline.h>
 #define TAS(lock) _Asm_xchg(_SZ_W, lock, 1, _LDHINT_NONE)
+#define TAS_SPIN(lock) (*(lock) ? 1 : TAS(lock))
 
 #endif /* HPUX on IA64, non gcc */
 
@@ -925,6 +934,10 @@ extern int tas(volatile slock_t *lock);        /* in port/.../tas.s, or
 #define TAS(lock)      tas(lock)
 #endif  /* TAS */
 
+#if !defined(TAS_SPIN)
+#define TAS_SPIN(lock) TAS(lock)
+#endif  /* TAS_SPIN */
+
 
 /*
  * Platform-independent out-of-line support routines