GCC原子内存访问操作性能与InnoDB存储引擎

GCC提供了一系列内置的原子内存操作函数[1]。从InnoDB Plugin开始,InnoDB团队已经针对此特性进行了大量的优化。例如对于mutex的实现使用了函数__sync_lock_test_and_set,对于一些严格要求的自增环境,如innodb_thread_concurrency,使用了函数__sync_add_and_fetch。

早在2008年就有人提出了通过GCC内置的原子内存操作的优化建议[2],从其的名字看貌似还是一个中国人。而这个Feature request甚至引起了InnoDB存储引擎的创始人Heikki Tuuri和原MySQL(现MariaDB)团队资深开发人员Sergei Golubchik的讨论。并最终在InnoDB Plugin 1.0.3中引入了这块代码。

为了得知GCC内置的这些原子内存访问操作到底能带来多少的性能提升,我进行了如下的性能基准测试,这里使用自增进行测试。无锁的方式进行非线程安全地自增操作,通过pthread_mutex_t进行线程安全的自增操作,以及通过函数__sync_add_and_fetch进行线程安全的操作。测试分为1、32、128、1024个线程进行测试。基准测试代码如下所示:

#include

#include <sys/time.h>
#include
#include

int n_thd;
int n_count = 100000000;
int sum = 0;
pthread_mutex_t lock = PTHREAD_MUTEX_INITIALIZER;

double
gettime(void)
{
  struct timeval tv;
  if (gettimeofday(&tv, NULL))
  {
    perror("gettimeofday()");
    return -1;
  }
  return (double)tv.tv_sec + (double)tv.tv_usec*1e-6;
}

static void* increment_with_lock(void* args) {
	int i = 0;
	for(i = 0; i < n_count/n_thd; i++) {
		pthread_mutex_lock(&lock);
		sum++;
		pthread_mutex_unlock(&lock);
	}
}

static void* increment_with_no_lock(void* args) {
	int i = 0;
	for(i = 0; i < n_count/n_thd; i++) {
		sum++;
	}
}

static void* increment_with_atomic(void* args) {
	int i = 0;
	for(i = 0; i < n_count/n_thd; i++) {
		__sync_add_and_fetch(&sum, 1);
	}
}

int main(int argc, char* argv[]) {

	pthread_t thds[2048];
	int i;
	double start;

	if (argc == 1) {
		n_thd = 1;
	} else if (argc == 2) {
		n_thd = atoi(argv[1]);
	} else {
		n_thd = atoi(argv[1]);
		n_count = atoi(argv[2]);
	}

	start = gettime();
	for(i=0; i<n_thd; i++) {
		pthread_create(&thds[i], NULL,
#if 0
			increment_with_lock,
#elif 0
			increment_with_no_lock,
#elif 1
			increment_with_atomic,
#endif
			NULL);

	}

	for(i=0; i<n_thd; i++){
		pthread_join(thds[i],NULL);
	}

	printf("sum is: %d.\n",sum);
	printf("test average cost time: %.2f ns.\n",
			1000000000.0*(gettime() - start)/n_count);

	return 1;
}

最终测试的结果如下图所示:

GCC原子内存访问操作性能与InnoDB存储引擎-MySQL社区 Inside MySQL Group

可以发现,GCC所提供的原子内存访问操作的确比pthread_mutex_t的方式来的性能要好,多线程测试差不多能有3倍左右的性能提升。但是对比无锁方式还是有巨大的开销(当然这可以理解)。因此对于一些要求不高的自增统计,还是继续无赖地使用无锁自增吧。

然而,使用GCC内置的原子内存访问特性,应该非常小心,你必须完全确定你的CPU支持这个特性。例如函数__sync_lock_test_and_set,gcc的文档中有这样的一段说明:

Many targets have only minimal support for such locks, and do not support a full exchange operation. In this case, a target may support reduced functionality here by which the only valid value to store is the immediate constant 1. The exact value actually stored in *ptr is implementation defined.

例如PowerPC和ARM CPU可能仅部分支持这个特性,所以这会导致InnoDB运行的正确性。Yasufumi Kinoshita在InnoDB Plugin发布时就已经提出过该问题[3],Stewart Smith最近也详细说明了这个问题,并验证的确可以非常容易地在PowerPC上进行重现该问题[4]

参考资料

  1. http://gcc.gnu.org/onlinedocs/gcc-4.4.5/gcc/Atomic-Builtins.html
  2. http://bugs.mysql.com/bug.php?id=34175
  3. http://bugs.mysql.com/bug.php?id=47213
  4. http://lists.mysql.com/internals/38786
发表评论

坐等沙发
相关文章
MySQL 8.0 200W QPS!!!InnoDB大重构 #M1005#
MySQL 8.0 200W QPS!!!InnoDB大重构 …
WTF?MySQL DBA技术难度低为什么工资比Oracle高?
WTF?MySQL DBA技术难度低为什么工资比O…
MySQL 5.7新特性之在线收缩undo表空间
MySQL 5.7新特性之在线收缩undo表空间
这一刻,MySQL 8终于追赶上了Oracle 8 · 降序索引
这一刻,MySQL 8终于追赶上了Oracle 8 ·…
原谅我这么幼稚,所以才会喜欢你这麼久 #MySQL#
原谅我这么幼稚,所以才会喜欢你这麼久 …
为什么我再不看好MariaDB
为什么我再不看好MariaDB
Oracle MySQL ACE. Author of Inside MySQL and MySQL Core Series. Great at MySQL performance tuning、troubleshooting、systems availability and scalability、capacity planning, etc.

一触即发,2017年,数据库世界的诸神之战