List:Commits« Previous MessageNext Message »
From:Vladislav Vaintroub Date:September 6 2010 12:09pm
Subject:bzr commit into mysql-trunk branch (vvaintroub:3191) Bug#56585
View as plain text  
#At file:///H:/bzr-new/mysql-trunk/ based on revid:alik@stripped

 3191 Vladislav Vaintroub	2010-09-06
      Bug#56585: Slowdown of readonly sysbench benchmarks (e.g point_select) 
      on Windows  5.5
      
      Problem:
      The reason for the bug, as identified using xperf profiler is heavy 
      context switching in reader-writer lock implementation. The (portable 
      but slow) implementation of rwlock on makes use of 2 condition variables
      (non-native, implemented with events and critical section) and a mysql 
      "mutex" (critical section on Wndows). This amounts to a heavy-weight object
      consisting of 6 Windows events and 3 critical sections. Profiling shows 
      the #1 context  switching when runnng sysbench readonly tests is 
      WaitForSingleObject/SetEvent on events inside rwlock inside MDL.
      
      Solution is to use native reader/writer locks aka slim rwlock on Windows,
      that are available since Vista, that are very fast. A set of new rwlock 
      related functions (my_win_rwlock_xxx) is implemented in this patch, that 
      will use slim reader-writer locks when available (Vista+) and fallback to
      old implementation if not available. This functions will be used for MySQL 
      reader-writer lock and also for the special pr("prefer reader") rwlock, that
      is used in MDL.
      
      How "prefer reader" functionality is implemented:
      We track count of pending reader requests.  Write lock is taken as usual,
      but if it finds that there are pending readers, it gives up the lock, yields
      and retries.
      
      This fulfills the requirement for MDL "prefer reader": write lock must not
      be given if there are pending readers.
      
      Benchmark on 8 core machine (sysbench, point_select, 256 users) shows 
      improvement by ~30% with this patch(18000 TPS with patch vs 14000 TPS 
      without)

    added:
      mysys/my_win_rwlock.c
    modified:
      include/my_pthread.h
      mysys/CMakeLists.txt
      mysys/thr_rwlock.c
=== modified file 'include/my_pthread.h'
--- a/include/my_pthread.h	2010-08-20 08:48:59 +0000
+++ b/include/my_pthread.h	2010-09-06 12:08:56 +0000
@@ -583,6 +583,17 @@ int my_pthread_fastmutex_lock(my_pthread
 /* Use our own version of read/write locks */
 #define NEED_MY_RW_LOCK 1
 #define rw_lock_t my_rw_lock_t
+#ifdef _WIN32
+#define my_rwlock_init(A,B) win_rw_init((A), 0)
+#define rw_rdlock(A) win_rw_rdlock((A))
+#define rw_wrlock(A) win_rw_wrlock((A))
+#define rw_tryrdlock(A) win_rw_tryrdlock((A))
+#define rw_trywrlock(A) win_rw_trywrlock((A))
+#define rw_unlock(A) win_rw_unlock((A))
+#define rwlock_destroy(A) win_rw_destroy((A))
+#define rw_lock_assert_write_owner(A) 
+#define rw_lock_assert_not_write_owner(A)
+#else
 #define my_rwlock_init(A,B) my_rw_init((A), 0)
 #define rw_rdlock(A) my_rw_rdlock((A))
 #define rw_wrlock(A) my_rw_wrlock((A))
@@ -592,6 +603,7 @@ int my_pthread_fastmutex_lock(my_pthread
 #define rwlock_destroy(A) my_rw_destroy((A))
 #define rw_lock_assert_write_owner(A) my_rw_lock_assert_write_owner((A))
 #define rw_lock_assert_not_write_owner(A) my_rw_lock_assert_not_write_owner((A))
+#endif
 #endif /* USE_MUTEX_INSTEAD_OF_RW_LOCKS */
 
 
@@ -623,6 +635,17 @@ extern int rw_pr_init(rw_pr_lock_t *);
 struct st_my_rw_lock_t;
 #define rw_pr_lock_t my_rw_lock_t
 extern int rw_pr_init(struct st_my_rw_lock_t *);
+
+#ifdef _WIN32
+#define rw_pr_rdlock(A) win_rw_rdlock((A))
+#define rw_pr_wrlock(A) win_rw_wrlock((A))
+#define rw_pr_tryrdlock(A) win_rw_tryrdlock((A))
+#define rw_pr_trywrlock(A) win_rw_trywrlock((A))
+#define rw_pr_unlock(A) win_rw_unlock((A))
+#define rw_pr_destroy(A) win_rw_destroy((A))
+#define rw_pr_lock_assert_write_owner(A)
+#define rw_pr_lock_assert_not_write_owner(A)
+#else
 #define rw_pr_rdlock(A) my_rw_rdlock((A))
 #define rw_pr_wrlock(A) my_rw_wrlock((A))
 #define rw_pr_tryrdlock(A) my_rw_tryrdlock((A))
@@ -640,14 +663,18 @@ extern int rw_pr_init(struct st_my_rw_lo
   read/write locks which prefer readers we have to use own implementation.
 */
 typedef struct st_my_rw_lock_t {
-	pthread_mutex_t lock;		/* lock for structure		*/
-	pthread_cond_t	readers;	/* waiting readers		*/
-	pthread_cond_t	writers;	/* waiting writers		*/
-	int		state;		/* -1:writer,0:free,>0:readers	*/
-	int             waiters;        /* number of waiting writers	*/
-	my_bool         prefer_readers;
+#ifdef _WIN32
+  SRWLOCK srw; /* native reader writer lock */
+  LONG pending_readers;
+#endif
+  pthread_mutex_t lock;        /* lock for structure */
+  pthread_cond_t  readers;     /* waiting readers */
+  pthread_cond_t  writers;     /* waiting writers */
+  int  state;                  /* -1:writer,0:free,>0:readers	*/
+  int  waiters;                /* number of waiting writers	*/
+  my_bool  prefer_readers;
 #ifdef SAFE_MUTEX
-        pthread_t       write_thread;
+  pthread_t       write_thread;
 #endif
 } my_rw_lock_t;
 
@@ -658,6 +685,19 @@ extern int my_rw_wrlock(my_rw_lock_t *);
 extern int my_rw_unlock(my_rw_lock_t *);
 extern int my_rw_tryrdlock(my_rw_lock_t *);
 extern int my_rw_trywrlock(my_rw_lock_t *);
+
+#ifdef _WIN32
+extern int win_rw_init(my_rw_lock_t *, my_bool *);
+extern int win_rw_destroy(my_rw_lock_t *);
+extern int win_rw_rdlock(my_rw_lock_t *);
+extern int win_rw_wrlock(my_rw_lock_t *);
+extern int win_rw_unlock(my_rw_lock_t *);
+extern int win_rw_tryrdlock(my_rw_lock_t *);
+extern int win_rw_trywrlock(my_rw_lock_t *);
+#endif
+
+#endif
+
 #ifdef SAFE_MUTEX
 #define my_rw_lock_assert_write_owner(A) \
   DBUG_ASSERT((A)->state == -1 && pthread_equal(pthread_self(), \

=== modified file 'mysys/CMakeLists.txt'
--- a/mysys/CMakeLists.txt	2010-08-12 15:27:53 +0000
+++ b/mysys/CMakeLists.txt	2010-09-06 12:08:56 +0000
@@ -37,7 +37,8 @@ SET(MYSYS_SOURCES  array.c charset-def.c
 				my_rdtsc.c waiting_threads.c)
 
 IF (WIN32)
- SET (MYSYS_SOURCES ${MYSYS_SOURCES} my_winthread.c my_wincond.c my_winerr.c my_winfile.c my_windac.c my_conio.c)
+ SET (MYSYS_SOURCES ${MYSYS_SOURCES} my_winthread.c my_wincond.c my_winerr.c my_winfile.c my_windac.c my_conio.c
+ my_win_rwlock.c)
 ENDIF()
 
 IF(HAVE_ALARM)

=== added file 'mysys/my_win_rwlock.c'
--- a/mysys/my_win_rwlock.c	1970-01-01 00:00:00 +0000
+++ b/mysys/my_win_rwlock.c	2010-09-06 12:08:56 +0000
@@ -0,0 +1,327 @@
+/* Copyright (c) 2007, 2010, Oracle and/or its affiliates. All rights reserved.
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; version 2 of the License.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program; if not, write to the Free Software Foundation,
+   51 Franklin Street, Suite 500, Boston, MA 02110-1335 USA */
+
+
+/*
+  Reader/writer lock functions on Windows.
+  Thus module implements win_rw_xxx functions (win_rw_rwlock, win_rw_rdlock etc)
+
+  Efficient implementation or rwlocks (Slim Reader Writer Locks) is provided by 
+  the OS starting with Vista. We will use this implementation if available. 
+  On older OS versions all functions will be just wrap the older portable 
+  (and slow) my_rw_xxx functionality.
+
+  Unusual in this implementation is supporting a special mode, "prefer reader" 
+  option, for which "force reader" would be more descriptive. Writer lock is not 
+  granted lock as long as there are pending reader requests. We track the count 
+  of pending reads. Writer would lock as usual, but it would give up the lock if 
+  it find that there are pending readers. This implementation would normally 
+  starve the writer, but in special cases this option is used it is not a 
+  problem.
+
+  Initialization (loading function pointers from OS libraries etc) happens first
+  time win_rw_init() is called, it is assumed to be the first call to this 
+  module (if it is not so it is a  programming error that is punished with
+  abort())
+
+*/
+
+#include "mysys_priv.h"
+#include <errno.h>
+
+/* Prototypes and function pointers for windows  functions */
+typedef VOID (WINAPI* srw_func) (PSRWLOCK SRWLock);
+typedef BOOL (WINAPI* srw_bool_func) (PSRWLOCK SRWLock);
+
+static srw_func pInitializeSRWLock;
+static srw_func pAcquireSRWLockExclusive;
+static srw_func pReleaseSRWLockExclusive;
+static srw_func pAcquireSRWLockShared;
+static srw_func pReleaseSRWLockShared;
+
+static srw_bool_func pTryAcquireSRWLockExclusive;
+static srw_bool_func pTryAcquireSRWLockShared;
+
+static int win_rw_init_first_call(my_rw_lock_t *rwl, my_bool *prefer_reader);
+static int dummy_rw_func(my_rw_lock_t *rwl);
+
+
+/* 
+ Function pointers to rwlock implementation, initialized by the first call to
+ win_rw_init()
+*/
+typedef int (*my_rwlock_func)(my_rw_lock_t *rwl);
+
+static int (*rw_init_impl)(my_rw_lock_t *rwl, my_bool *prefer_reader)= 
+  win_rw_init_first_call;
+static my_rwlock_func rw_destroy_impl= dummy_rw_func;
+static my_rwlock_func rw_wrlock_impl= dummy_rw_func;
+static my_rwlock_func rw_rdlock_impl= dummy_rw_func;
+static my_rwlock_func rw_unlock_impl= dummy_rw_func;
+static my_rwlock_func rw_tryrdlock_impl= dummy_rw_func;
+static my_rwlock_func rw_trywrlock_impl= dummy_rw_func;
+
+
+/* Slim reader writer (SRW) lock implementation*/
+static int srw_init(my_rw_lock_t *rwl, my_bool *prefer_reader);
+static int srw_destroy(my_rw_lock_t *rwl);
+static int srw_rdlock(my_rw_lock_t *rwl);
+static int srw_wrlock(my_rw_lock_t *rwl);
+static int srw_unlock(my_rw_lock_t *rwl);
+
+
+static int srw_init(my_rw_lock_t *rwl, my_bool *prefer_reader_attr)
+{
+  pInitializeSRWLock(&rwl->srw);
+  rwl->pending_readers= 0;
+  if(prefer_reader_attr)
+    rwl->prefer_readers= (*prefer_reader_attr)? 1: 0;
+  else
+    rwl->prefer_readers= 0;
+
+  return 0;
+}
+
+static int srw_destroy(my_rw_lock_t *rwl)
+{
+  return 0;
+}
+
+static int srw_rdlock(my_rw_lock_t *rwl)
+{
+  if(rwl->prefer_readers)
+  {
+    /* Track pending readers (writer lock  needs to know about it)*/
+    InterlockedIncrement(&rwl->pending_readers);
+    pAcquireSRWLockShared(&rwl->srw);
+    InterlockedDecrement(&rwl->pending_readers);
+  }
+  else
+  {
+    pAcquireSRWLockShared(&rwl->srw);
+  }
+  rwl->state = 1;
+  return 0;
+}
+
+static int srw_wrlock(my_rw_lock_t *rwl)
+{
+  if(!rwl->prefer_readers)
+  {
+    pAcquireSRWLockExclusive(&rwl->srw);
+    rwl->state = -1;
+    return 0;
+  }
+
+  for(;;)
+  {
+    pAcquireSRWLockExclusive(&rwl->srw);
+
+    if(rwl->pending_readers == 0)
+      break;
+
+    /* 
+      By the very special definition of "prefer readers", writer lock must not 
+      be acquired while there are pending readers. So we have to release lock we 
+      just got, and retry until no pending readers.
+    */
+
+    pReleaseSRWLockExclusive(&rwl->srw);
+    SwitchToThread(); /* yield execution to another thread */
+  }
+  rwl->state = -1;
+  return 0;
+}
+
+
+static int srw_unlock(my_rw_lock_t *rwl)
+{
+  if(rwl->state == -1)
+  {
+    /* Was locked by writer */
+    pReleaseSRWLockExclusive(&rwl->srw);
+  }
+  else if(rwl->state == 1)
+  {
+    /* Was locked by reader */
+    pReleaseSRWLockShared(&rwl->srw);
+  }
+  else
+    DBUG_ASSERT(0); // lock not taken
+  return 0;
+}
+
+static int srw_trywrlock(my_rw_lock_t *rwl)
+{
+
+  /*
+    "TryAcquire" functions were not available on Vista, only Win7, 
+    so in case of Vista we just execute lock without trying
+  */
+  if (pTryAcquireSRWLockExclusive) 
+  {
+    /* TryAcquire available */
+    if(!pTryAcquireSRWLockExclusive(&(rwl->srw)))
+      return EBUSY;
+  }
+  else
+  {
+     pAcquireSRWLockExclusive(&(rwl->srw));
+  }
+
+  /* 
+    Unlock if there are pending readers, and readers are preferred (actually
+    forced)
+  */
+  if(rwl->pending_readers)
+  {
+      pReleaseSRWLockExclusive(&(rwl->srw));
+      return EBUSY;
+  }
+  return 0;
+}
+
+int srw_tryrdlock(my_rw_lock_t *rwl)
+{
+  BOOL success;
+  if(rwl->prefer_readers)
+    InterlockedIncrement(&(rwl->pending_readers));
+
+  if(pTryAcquireSRWLockShared)
+  {
+    success= pTryAcquireSRWLockShared(&(rwl->srw));
+  }
+  else
+  {
+    pAcquireSRWLockShared(&rwl->srw);
+    success= TRUE;
+  }
+
+  if (rwl->prefer_readers)
+    InterlockedDecrement(&(rwl->pending_readers));
+
+  return success;
+}
+
+
+static int dummy_rw_func(my_rw_lock_t *rwl)
+{
+  /*
+    If someone happen to call this function, it points to programming
+    error (using rwlock prior to initialization win_rw_init).
+  */
+  abort();
+  return 1;
+}
+
+static void load_function_pointers(void)
+{
+  HMODULE hModule;
+  hModule = GetModuleHandle("kernel32");
+
+  pInitializeSRWLock= (srw_func) GetProcAddress(hModule,
+    "InitializeSRWLock");
+
+  pAcquireSRWLockExclusive= (srw_func) GetProcAddress(hModule,
+    "AcquireSRWLockExclusive");
+  pAcquireSRWLockShared= (srw_func) GetProcAddress(hModule,
+    "AcquireSRWLockShared");
+
+  pReleaseSRWLockExclusive= (srw_func) GetProcAddress(hModule,
+    "ReleaseSRWLockExclusive");
+  pReleaseSRWLockShared= (srw_func) GetProcAddress(hModule,
+    "ReleaseSRWLockShared");
+
+  pTryAcquireSRWLockExclusive =  (srw_bool_func) GetProcAddress(hModule,
+    "TryAcquireSRWLockExclusive");
+  pTryAcquireSRWLockShared =  (srw_bool_func) GetProcAddress(hModule,
+    "TryAcquireSRWLockShared");
+
+  if (pInitializeSRWLock)
+  {
+    /* SRW functions could be loaded, use SRW for locks */
+    rw_init_impl= srw_init;
+    rw_destroy_impl= srw_destroy;
+    rw_rdlock_impl= srw_rdlock;
+    rw_wrlock_impl= srw_wrlock;
+    rw_unlock_impl= srw_unlock;
+    rw_tryrdlock_impl= srw_tryrdlock;
+    rw_trywrlock_impl= srw_trywrlock;
+  }
+  else
+  {
+    /* Old windows, use my_rw_xxx */
+    rw_init_impl= my_rw_init;
+    rw_destroy_impl= my_rw_destroy;
+    rw_rdlock_impl= my_rw_rdlock;
+    rw_wrlock_impl= my_rw_wrlock;
+    rw_unlock_impl= my_rw_unlock;
+    rw_tryrdlock_impl= my_rw_tryrdlock;
+    rw_trywrlock_impl= my_rw_trywrlock;
+  }
+}
+
+
+/* 
+ First call to win_rw_init, loads function pointers, figures out which implementation
+ to use
+*/
+static int win_rw_init_first_call(my_rw_lock_t *rw, my_bool *prefer_reader)
+{
+  static my_pthread_once_t once_control = MY_PTHREAD_ONCE_INIT;
+  my_pthread_once(&once_control, load_function_pointers);
+
+  return win_rw_init(rw, prefer_reader);
+}
+
+
+
+int win_rw_init(my_rw_lock_t *rwl, my_bool *prefer_reader)
+{
+  return rw_init_impl(rwl, prefer_reader);
+}
+
+int win_rw_destroy(my_rw_lock_t *rwl)
+{
+  return rw_destroy_impl(rwl);
+}
+
+int win_rw_rdlock(my_rw_lock_t *rwl)
+{
+  return rw_rdlock_impl(rwl);
+}
+
+int win_rw_wrlock(my_rw_lock_t *rwl)
+{
+  return rw_wrlock_impl(rwl);
+}
+
+int win_rw_tryrdlock(my_rw_lock_t *rwl)
+{
+  return rw_tryrdlock_impl(rwl);
+}
+
+int win_rw_trywrlock(my_rw_lock_t *rwl)
+{
+  return rw_trywrlock_impl(rwl);
+}
+
+int win_rw_unlock(my_rw_lock_t *rwl)
+{
+  return rw_unlock_impl(rwl);
+}
+
+
+

=== modified file 'mysys/thr_rwlock.c'
--- a/mysys/thr_rwlock.c	2010-08-12 13:50:23 +0000
+++ b/mysys/thr_rwlock.c	2010-09-06 12:08:56 +0000
@@ -192,11 +192,14 @@ int my_rw_unlock(my_rw_lock_t *rwp)
   return(0);
 }
 
-
 int rw_pr_init(struct st_my_rw_lock_t *rwlock)
 {
   my_bool prefer_readers_attr= TRUE;
+#ifdef _WIN32
+  return win_rw_init(rwlock, &prefer_readers_attr);
+#else
   return my_rw_init(rwlock, &prefer_readers_attr);
+#endif
 }
 
 #else


Attachment: [text/bzr-bundle] bzr/vvaintroub@mysql.com-20100906120856-f7bud5z19vjmm61u.bundle
Thread
bzr commit into mysql-trunk branch (vvaintroub:3191) Bug#56585Vladislav Vaintroub6 Sep