Boost :: mutex比没有mutex的程序花费的时间less

我已经执行了下面的程序,我创build了100个线程并发执行。 请注意,这是一个示例程序。 我明白下面的程序不需要multithreading,但我的意图是testing互斥

class ThreadPool{ public: ThreadPool(int num = 10); ~ThreadPool(); void AssignPool(); void doSometask(); void inc(); private: boost::asio::io_service ioService; boost::thread_group threadpool; boost::asio::io_service::work * work; volatile int p_size; int pool_sz; boost::mutex io_mutex;// with boost lock }; void ThreadPool::AssignPool() { std::cout<<std::endl<<"pool_sz="<<pool_sz<<std::endl; for(int i=0;i<pool_sz;i++) { ioService.post(boost::bind(&ThreadPool::doSometask, this)); } } void ThreadPool::inc() { p_size++; } void ThreadPool::doSometask() { // boost::mutex::scoped_lock lock(io_mutex); for(int i=0;i<10000;i++){ inc(); } } ThreadPool::ThreadPool(int num):p_size(0) { pool_sz = num; work = new boost::asio::io_service::work(ioService); for(int i =0;i<num;i++) { threadpool.create_thread(boost::bind(&boost::asio::io_service::run, &ioService )) ; } } ThreadPool::~ThreadPool() { delete work; ioService.stop(); threadpool.join_all(); } int main() { ThreadPool p1(100); p1.AssignPool(); } 

情况1:上述程序通过注释“boost :: mutex :: scoped_lock lock(io_mutex);” 这是“无互斥事件”的行。 程序所花费的时间是

 real 0m1.386s user 0m0.483s sys 0m9.937s 

情况2:与互斥体:但是,当我运行这个互斥体的程序,即“boost :: mutex :: scoped_lock锁(io_mutex);” 线。 这个程序花费的时间较less。

 real 0m0.289s user 0m0.067s sys 0m0.230s 

在我对互斥体的理解中,程序应该花费比没有互斥体更多的时间。 这里出了什么问题?

在你的例子中,你锁定了doSometask()的互斥doSometask() ,因此,一直只有一个线程正在运行,并且在完成另一个任务之前它将完成for循环。 因此,程序运行字面串行,并没有发生缓存脱粒。

如果没有锁定,所有线程在获得处理器时间的时候都会运行,假设处理器的数量明显低于100,那么所有级别的缓存打包都会进行(像Bo Persson在评论中所写的那样),而这个会增加运行时间。

测量锁定对运行时间的影响的更好的方法是(a)只运行与你的计算机具有核心数量一样多的线程,以便由于上下文切换而使高速缓存脱粒被最小化,并且(b)到ThreadPool::inc()方法中,以便更频繁地进行同步。

作为奖励,您可以通过将p_size声明为std::atomic<int> (C ++ 11)来正确运行无锁方法,并查看基于互斥的同步对使用原子的影响。

正如所评论的那样,“一个一个有序的方式”比每个人都快速地进行得更好,但不仅如此。 最主要的原因是你为每个线程在doSometask()里面工作的时间对于现代CPU来说太少了。 更新你的doSometask来做更多的工作,并且让你的线程减少对不断访问共享数据的依赖:

 #include <iostream> #include <chrono> #include <atomic> #include <boost/asio/io_service.hpp> #include <boost/thread.hpp> class ThreadPool { public: ThreadPool(int num = 10, int cycles = 10000); ~ThreadPool(); void inc(volatile int* x); void AssignPool(); void doSometask(volatile int* x); void AssignPoolSync(); void doSometaskSync(volatile int* x); private: boost::asio::io_service ioService; boost::thread_group threadpool; boost::asio::io_service::work * work; std::atomic<int> p_size; int *xsize; int pool_sz, cycles; boost::mutex io_mutex; // with boost lock }; void ThreadPool::AssignPool() { for (int i = 0; i<pool_sz; ++i) ioService.post(boost::bind(&ThreadPool::doSometask, this, &xsize[i])); } void ThreadPool::AssignPoolSync() { for (int i=0; i<pool_sz; ++i) ioService.post(boost::bind(&ThreadPool::doSometaskSync, this, &xsize[i])); } void ThreadPool::inc(volatile int* x) { *x = *x + 1; } void ThreadPool::doSometask(volatile int* x) { for (int i=0; i<cycles; ++i) { inc(x); if (i & 255 == 0) p_size++; // access shared data evert 256 cycles } } void ThreadPool::doSometaskSync(volatile int* x) { boost::mutex::scoped_lock lock(io_mutex); doSometask(x); } ThreadPool::ThreadPool(int num, int cycles) { pool_sz = num; p_size = 0; this->cycles = cycles; xsize = new int[num]; memset(xsize, 0, num * sizeof(int)); work = new boost::asio::io_service::work(ioService); for (int i=0; i<pool_sz; ++i) threadpool.create_thread(boost::bind(&boost::asio::io_service::run, &ioService)); } ThreadPool::~ThreadPool() { delete work; ioService.stop(); threadpool.join_all(); delete[] xsize; } int main(int argc, const char** argv) { const int C = argc>1 ? std::stoi(argv[1]) : 10000; // number of cycles const int T = argc>2 ? std::stoi(argv[2]) : 100; // number of threads const int N = argc>3 ? std::stoi(argv[3]) : 50; // number of times to time execution long long t_min[2] = {0}; for (int i = 0; i<N*2; ++i) { auto t0 = std::chrono::high_resolution_clock::now(); { Sleep(1); ThreadPool pool(T, C); if (i&1) pool.AssignPoolSync(); else pool.AssignPool(); } auto t1 = std::chrono::high_resolution_clock::now(); t_min[i&1] = std::min(i>1 ? t_min[i&1] : (t1-t0).count(), (t1-t0).count()); } printf("timeSync / time: %f\n", (t_min[1] + 0.0) / (t_min[0] + 0.0)); } 

使用这个测试你可以模拟更好的实际工作:线程运行的工作大多是独立的,有时他们访问共享数据。 您也可以使用不同的参数来运行它,以更改每个线程运行的循环次数和线程数。

这些是我在4核心PC上运行时得到的示例结果:

 test> test.exe 10000 100 timeSync / time: 1.027782 test> test.exe 500000 100 timeSync / time: 3.531433 

换句话说,当每个线程只有10000个周期时,同步版本几乎和非同步一样快,但是我把周期数增加到了500000,然后同步版本慢了3.5倍

我既不是计算机科学家也不是操作系统专家。 但是每当我试图比较两个类似函数的性能,而不是比较单次执行的时间,我多次运行函数并比较平均值(我的这种做法我错了,大部分时间都适用于我。打开输入/评论从这方面的专家)。 我背后的想法是,因为我正在使用操作系统,所以资源(主要是处理器)没有完全分配给正在观察的应用程序。 他们被许多其他进程同时共享。

我试图做你的应用程序相同,得到低于执行1000次以上的应用程序的结果。

nomutex:11.97用户| 5.76系统| 0:20.55逝去| 86%的CPU

withmutex:30.78 user | 8.78系统| 0:43.67逝去| 90%的CPU

而现在大多数设备都有多核CPU,所以我使用下面的链接强制操作系统只使用单核。 https://unix.stackexchange.com/a/23109

希望这会帮助你。