为什么8个线程比2个线程慢?

时间:2016-06-27 19:03:11

标签: c++ multithreading tbb tsx

我必须先为我糟糕的英语道歉。 我现在正在学习硬件事务内存,我在TBB中使用spin_rw_mutex.h来实现C ++中的事务块。 speculative_spin_rw_mutex是spin_rw_mutex.h中的一个类,它是一个互斥体,已经实现了intel TSX的RTM接口。

我用来测试RTM的例子非常简单。我创建了帐户类,我随机将钱从一个帐户转移到另一个帐户。所有帐户都在一个帐户数组中,大小为100.随机函数处于提升状态。(我认为STL具有相同的随机函数)。传递函数受speculative_spin_rw_mutex保护。我使用tbb :: parallel_for和tbb :: task_scheduler_init来控制并发。所有传输方法都在paraller_for的lambda中调用。总转移时间为100万。奇怪的是当task_scheduler_init设置为2时,程序是最快的(8秒)。实际上我的CPU是i7 6700k,有8个线程。在8和50,000的范围内,程序的性能几乎没有变化(11到12秒)。当我将task_scheduler_init增加到100,000时,运行时间将增加到大约18秒。 我试图使用分析器来分析程序,我发现热点功能是互斥锁。但我认为交易回滚的速度并不高。我不知道为什么程序这么慢。

有人说虚假共享会降低性能,因此我尝试使用

的std ::矢量> cache_aligned_accounts(AccountsSIZE,帐户(1000));

替换原始数组

帐户*帐户[AccountsSIZE];

避免虚假分享。似乎没有任何改变; 这是我的新代码。

#include <tbb/spin_rw_mutex.h> #include <iostream> #include "tbb/task_scheduler_init.h" #include "tbb/task.h" #include "boost/random.hpp" #include <ctime> #include <tbb/parallel_for.h> #include <tbb/spin_mutex.h> #include <tbb/cache_aligned_allocator.h> #include <vector> using namespace tbb; tbb::speculative_spin_rw_mutex mu; class Account { private: int balance; public: Account(int ba) { balance = ba; } int getBalance() { return balance; } void setBalance(int ba) { balance = ba; } }; //Transfer function. Using speculative_spin_mutex to set critical section void transfer(Account &from, Account &to, int amount) { speculative_spin_rw_mutex::scoped_lock lock(mu); if ((from.getBalance())<amount) { throw std::invalid_argument("Illegal amount!"); } else { from.setBalance((from.getBalance()) - amount); to.setBalance((to.getBalance()) + amount); } } const int AccountsSIZE = 100; //Random number generater and distributer boost::random::mt19937 gener(time(0)); boost::random::uniform_int_distribution<> distIndex(0, AccountsSIZE - 1); boost::random::uniform_int_distribution<> distAmount(1, 1000); /* Function of transfer money */ void all_transfer_task() { task_scheduler_init init(10000);//Set the number of tasks can be run together /* Initial accounts, using cache_aligned_allocator to avoid false sharing */ std::vector<Account, cache_aligned_allocator<Account>> cache_aligned_accounts(AccountsSIZE,Account(1000)); const int TransferTIMES = 10000000; //All transfer tasks parallel_for(0, TransferTIMES, 1, [&](int i) { try { transfer(cache_aligned_accounts[distIndex(gener)], cache_aligned_accounts[distIndex(gener)], distAmount(gener)); } catch (const std::exception& e) { //cerr << e.what() << endl; } //std::cout << distIndex(gener) << std::endl; }); std::cout << cache_aligned_accounts[0].getBalance() << std::endl; int total_balance = 0; for (size_t i = 0; i < AccountsSIZE; i++) { total_balance += (cache_aligned_accounts[i].getBalance()); } std::cout << total_balance << std::endl; }

2 个答案:

答案 0 :(得分:2)

由于英特尔TSX在缓存行粒度上工作,因此虚假共享绝对是首要任务。不幸的是,cache_aligned_allocator不是你可能期望的,即它对齐整个std :: vector,但是你需要单独的Account来占用整个缓存行以防止错误共享。

答案 1 :(得分:1)

虽然我无法重现您的基准测试,但我在此处看到了导致此行为的两种可能原因:

  • &#34;太多的厨师煮汤了#34;你使用一个 spin_rw_mutex ,被所有线程的所有转移锁定。在我看来,您的传输顺序执行。这可以解释为什么配置文件会在那里看到热点。在这种情况下,英特尔页面会警告性能下降。

  • 吞吐量与速度:在i7上,in a couple of benchmarks, I could notice当你使用更多核心时,每个核心的运行速度会慢一点,因此固定的siez循环的总体时间会更长。然而,计算总吞吐量(即,在所有这些并行循环中发生的事务的总数),吞吐量要高得多(尽管与核的数量不完全成比例)。

我宁愿选择第一种情况,但第二种情况不是要消除。