自动并行相关技术:
Cilk学习资料:
Cilk Plus, deprecated for 7.x, will not be in 8.x.
cilk在后续版本中似乎已经被抛弃了,https://gcc.gnu.org/bugzilla/show_bug.cgi?id=81828
相关笔记
粒度设置
例如如下:
#include <iostream>
#include <cilk/cilk.h>
#include <cilk/reducer_opadd.h>
#include <cilk/reducer_min.h>
#include <cilk/reducer_max.h>
#include <cilk/cilk_api.h>
#include <vector>
#include <set>
#include <unordered_set>
#include <mutex>
#include "omp.h"
#include <random>
using namespace grape;
void test_cilk_grainsize(){
int cnt = 1e8;
std::unordered_set<int> thread_ids;
std::mutex set_mux_;
std::atomic<int> node_id(0);
// cilk
std::cout << "--------------------cilk--------------------------\n";
std::cout << "__cilkrts_get_nworkers=" << __cilkrts_get_nworkers() << std::endl;
double s_time = GetCurrentTime();
#pragma cilk grainsize = 1
cilk_for(int i = 0; i < cnt; i++){
int thread_id = __cilkrts_get_worker_number();
node_id++;
}
std::cout << "node_id=" << node_id << std::endl;
std::cout << "time1=" << (GetCurrentTime()-s_time) << std::endl;
s_time = GetCurrentTime();
// #pragma cilk grainsize = 1
node_id = 0;
cilk_for(int i = 0; i < cnt; i++){
int thread_id = __cilkrts_get_worker_number();
node_id++;
}
std::cout << "node_id=" << node_id << std::endl;
std::cout << "time2=" << (GetCurrentTime()-s_time) << std::endl;
s_time = GetCurrentTime();
node_id = 0;
#pragma cilk grainsize = 1
cilk_for(int i = 0; i < cnt; i++){
int thread_id = __cilkrts_get_worker_number();
node_id++;
}
std::cout << "node_id=" << node_id << std::endl;
std::cout << "time3=" << (GetCurrentTime()-s_time) << std::endl;
}
int main(int argc, char* argv[])
{
// test_add();
// test_min();
// test_max();
// test_thread_id();
test_cilk_grainsize();
return 0;
}
其中grainsize只对下面第一个for循环有效。
输出:
--------------------cilk--------------------------
__cilkrts_get_nworkers=6
node_id=100000000
time1=3.78073
node_id=100000000
time2=1.76205
node_id=100000000
time3=3.77184
完整测试代码
#include <iostream>
#include <cilk/cilk.h>
#include <cilk/reducer_opadd.h>
#include <cilk/reducer_min.h>
#include <cilk/reducer_max.h>
#include <cilk/cilk_api.h>
#include <vector>
#include <set>
#include <unordered_set>
#include <mutex>
#include "omp.h"
#include "../lib/parallel_engine.h"
#include "../lib/util.h"
#include <random>
using namespace grape;
std::random_device rd;
std::mt19937 mt(rd());
std::uniform_int_distribution<int> dist(-1000, 1000);
auto rnd = std::bind(dist, mt);
unsigned int compute(cilk::reducer_opadd<float>& total, float v)
{
// return i; // 通过i计算返回值
total += v;
}
void test_add(){
cilk::reducer_opadd<float> total(0);
size_t n = 10000;
std::cout << "total init = " << total.get_value() << std::endl;
cilk_for(unsigned int i = 1; i <= n; ++i)
{
// // total += compute(i);
// total += 1e-9;
compute(total, 1e-9);
}
float correct = n * 1e-9;
if (total.get_value() == correct)
std::cout << "Total (" << total.get_value() << ") is correct" << std::endl;
else
std::cout << "Total (" << total.get_value() << ") is WRONG, should be "<< correct << " wc=" << (correct-total.get_value()) << std::endl;
}
void test_min(){
cilk::reducer< cilk::op_min<float> > rm;
float a[] = {0.0001, 0.2, 0.3, -0.1, -0.2, 1, 0};
cilk_for (int i = 0; i < 7; ++i)
{
rm->calc_min(a[i]); // or *rm = cilk::max_of(*max, a[i])
}
std::cout << "minimum value is " << rm.get_value() << std::endl;
}
void test_max(){
cilk::reducer< cilk::op_max<float> > rm;
float a[] = {0.0001, 0.2, 0.3, -0.1, -0.2, 1, 0};
cilk_for (int i = 0; i < 7; ++i)
{
rm->calc_max(a[i]); // or *rm = cilk::max_of(*max, a[i])
}
std::cout << "maximum value is " << rm.get_value() << std::endl;
}
void simulate_hard_computation() {
// std::this_thread::sleep_for(std::chrono::milliseconds(2000 + rnd()));
std::this_thread::sleep_for(std::chrono::milliseconds(1000));
}
void test_thread_id(){
// int cnt = 1e3;
int cnt = 100;
std::unordered_set<int> thread_ids;
std::mutex set_mux_;
std::atomic<int> node_id(0);
// cilk
std::cout << "--------------------cilk--------------------------\n";
std::cout << "__cilkrts_get_nworkers=" << __cilkrts_get_nworkers() << std::endl;
double s_time = GetCurrentTime();
cilk_for(int i = 0; i < cnt; i++){
int thread_id = __cilkrts_get_worker_number();
// std::cout << "tid=" << thread_id << " total_workers_num=" << __cilkrts_get_total_workers() << std::endl;
// std::unique_lock<std::mutex> lock(set_mux_);
// thread_ids.insert(thread_id);
node_id++;
simulate_hard_computation();
}
std::cout << "node_id=" << node_id << std::endl;
std::cout << "time1=" << (GetCurrentTime()-s_time) << std::endl;
std::cout << "thread_ids:\n";
for(auto id : thread_ids){
std::cout << id << ", ";
}
thread_ids.clear();
std::cout << std::endl;
// omp
std::cout << "--------------------omp--------------------------\n";
std::cout << "omp_get_num_threads=" << omp_get_num_threads() << std::endl; //当前线程组的线程数量
std::cout << "omp_get_max_threads=" << omp_get_max_threads() << std::endl; //当前线程组的线程数量
s_time = GetCurrentTime();
node_id = 0;
#pragma omp parallel for
for(int i = 0; i < cnt; i++){
int thread_id = omp_get_thread_num();
// std::cout << "tid=" << thread_id << " omp_get_num_threads=" << omp_get_num_threads() << std::endl;
// std::unique_lock<std::mutex> lock(set_mux_);
// thread_ids.insert(thread_id);
node_id++;
simulate_hard_computation();
}
std::cout << "node_id=" << node_id << std::endl;
std::cout << "time2=" << (GetCurrentTime()-s_time) << std::endl;
std::cout << "thread_ids:\n";
for(auto id : thread_ids){
std::cout << id << ", ";
}
std::cout << std::endl;
// use traditional thread by atomic operation
std::cout << "--------------------thread--------------------------\n";
ParallelEngine ple = ParallelEngine();
s_time = GetCurrentTime();
node_id = 0;
int thread_num = 6;
ple.ForEach(cnt, [&](int tid) {
int i = 0, step = 1; // step need to be adjusted
while(i < cnt){
i = node_id.fetch_add(step);
simulate_hard_computation();
}
}, thread_num
);
std::cout << "node_id=" << node_id << std::endl;
std::cout << "time3=" << (GetCurrentTime()-s_time) << std::endl;
std::cout << "-------------------------------------------------------\n";
}
void test_cilk_grainsize(){
int cnt = 1e8;
std::unordered_set<int> thread_ids;
std::mutex set_mux_;
std::atomic<int> node_id(0);
// cilk
std::cout << "--------------------cilk--------------------------\n";
std::cout << "__cilkrts_get_nworkers=" << __cilkrts_get_nworkers() << std::endl;
double s_time = GetCurrentTime();
#pragma cilk grainsize = 1
cilk_for(int i = 0; i < cnt; i++){
int thread_id = __cilkrts_get_worker_number();
node_id++;
}
std::cout << "node_id=" << node_id << std::endl;
std::cout << "time1=" << (GetCurrentTime()-s_time) << std::endl;
s_time = GetCurrentTime();
// #pragma cilk grainsize = 1
node_id = 0;
cilk_for(int i = 0; i < cnt; i++){
int thread_id = __cilkrts_get_worker_number();
node_id++;
}
std::cout << "node_id=" << node_id << std::endl;
std::cout << "time2=" << (GetCurrentTime()-s_time) << std::endl;
s_time = GetCurrentTime();
node_id = 0;
#pragma cilk grainsize = 1
cilk_for(int i = 0; i < cnt; i++){
int thread_id = __cilkrts_get_worker_number();
node_id++;
}
std::cout << "node_id=" << node_id << std::endl;
std::cout << "time3=" << (GetCurrentTime()-s_time) << std::endl;
}
int main(int argc, char* argv[])
{
// test_add();
// test_min();
// test_max();
// test_thread_id();
test_cilk_grainsize();
return 0;
}
//cmd: g++ -fcilkplus -lcilkrts -fopenmp test_parallel.cc -o test_parallel && ./test_parallel
// ref: https://ict.senecacollege.ca/~gpu621/pages/content/cilk__p.html