まず、私の英語が下手なことをお詫びしなければなりません。私は現在、ハードウェア トランザクション メモリを学習しており、TBB の spin_rw_mutex.h を使用して C++ でトランザクション ブロックを実装しています。speculative_spin_rw_mutex は、spin_rw_mutex.h 内のクラスです。h は、Intel TSX の RTM インターフェイスを既に実装しているミューテックスです。
RTM のテストに使用した例は非常に単純です。Account クラスを作成し、あるアカウントから別のアカウントにランダムに送金します。すべてのアカウントはアカウント配列にあり、サイズは 100 です。ランダム関数はブーストにあります (STL にも同じランダム関数があると思います)。伝達関数は、speculative_spin_rw_mutex で保護されています。tbb::parallel_for と tbb::task_scheduler_init を使用して並行性を制御しました。すべての転送メソッドは、paraller_for のラムダで呼び出されます。合計転送回数は 100 万回です。奇妙なことに、task_scheduler_init を 2 に設定すると、プログラムが最速 (8 秒) になります。実際、私のCPUは8スレッドのi7 6700kです。8 ~ 50,000 の範囲では、プログラムのパフォーマンスはほとんど変わりません (11 ~ 12 秒)。task_scheduler_init を 100,000 に増やすと、実行時間は約 18 秒に増加します。プロファイラーを使用してプログラムを分析しようとしたところ、ホットスポット機能がミューテックスであることがわかりました。ただし、トランザクションのロールバック率はそれほど高くないと思います。プログラムが遅い理由がわかりません。
偽の共有がパフォーマンスを低下させると誰かが言うので、その結果、私は使用しようとしました
std::vector> cache_aligned_accounts(AccountsSIZE,Account(1000));
元の配列を置き換える
アカウント* accounts[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;
}