我必须先为我糟糕的英语道歉。 我现在正在学习硬件事务内存,我在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;
}
答案 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循环的总体时间会更长。然而,计算总吞吐量(即,在所有这些并行循环中发生的事务的总数),吞吐量要高得多(尽管与核的数量不完全成比例)。
我宁愿选择第一种情况,但第二种情况不是要消除。