近期师兄让我学习了神威的混合编译部分,发现这真是个神奇的东西,可以不用再分两个文件写从核代码,也可以灵活运用宏来分别编译主从核部分、自动推断函数类型,甚至可以把对象从主核传送到从核(这真的很方便)。
神威主从核编译概述
- 编译器增加-mhybrid-coding选项,支持C++主从核代码在同文件内混合编写,默认为主核代码,从核入口函数增加attribute kernel属性。在编译时,分别使用-mslave和-mhost生成主从核对应的.o文件,最后将其链接起来。
- 没有被显示调用的主从核函数要增加arribute slave和arribute hostslave属性,具体见文档。
- 更多的细节见文档,本文主要就主从核crtp进行分享。
神威CRTP
本文中,首先编写了task基类,其中有Initialize()
,Finalize()
和 Self()
三个方法,基类中均是使用CRTP方式进行调用;接着编写了两个派生类,其Self()
方法分别对每个数组的数值加一或减一。主要代码如下所示:
task.h
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29
| #ifndef SWSPH_TASK_H_ #define SWSPH_TASK_H_ #include <vector>
namespace swsph {
template <typename T> class Task { public: Task(std::vector<int>& arr) : arr_{arr} {};
void Initialize() { static_cast<T*>(this)->Initialize(); }
void Finalize() { static_cast<T*>(this)->Finalize(); }
void Self(int index) { static_cast<T*>(this)->Self(index); }
std::vector<int>& arr_; };
};
#endif
|
task_acc.h
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44
| #ifndef SWSPH_TASK_ACC_BF_H_ #define SWSPH_TASK_ACC_BF_H_
#include "task.h"
namespace swsph {
class CalcAccBf : public Task<CalcAccBf> { public: CalcAccBf(std::vector<int>& arr) : Task<CalcAccBf>(arr), ref_arr(arr) {}
void Initialize();
void Self(int index);
void Finalize();
std::vector<int>& get_buf() { return acc_buf_; }
private: std::vector<int> acc_buf_; std::vector<int>& ref_arr; };
class CalcAccMinus : public Task<CalcAccMinus> { public: CalcAccMinus(std::vector<int>& arr) : Task<CalcAccMinus>(arr), ref_arr(arr){}
void Initialize();
void Self(int index);
void Finalize();
std::vector<int>& get_buf() { return acc_buf_; }
private: std::vector<int> acc_buf_; std::vector<int>& ref_arr; };
}
#endif
|
task_acc.cc
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30
| #include "task_acc_bf.h" #include <crts.h> #include <utility> #include <iostream>
namespace swsph { [[gnu::slave]] void CalcAccBf::Initialize() { printf("Slave %d, plus init.\n", _PEN); }
[[gnu::slave]] void CalcAccBf::Finalize() { printf("Slave %d, plus final.\n", _PEN); }
[[gnu::slave]] void CalcAccBf::Self(int index) { ref_arr[index]++; }
[[gnu::slave]] void CalcAccMinus::Initialize() { printf("Slave %d, minus init.\n", _PEN); }
[[gnu::slave]] void CalcAccMinus::Finalize() { printf("Slave %d, minus final.\n", _PEN); }
[[gnu::slave]] void CalcAccMinus::Self(int index) { ref_arr[index]--; } }
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67
| #include <crts.h> #include <vector> #include <iostream> #include "task.h" #include "task_acc_bf.h" #include "main.h"
#define SW64_SPAWN(fn, args) athread_spawn_cgs((fn), (args)) #define SW64_JOIN() athread_join_cgs()
#define VECTOR_LENGTH 128
template <typename T> [[gnu::slave]] void TimeIntegration::for_each_compute(swsph::Task<T>& task) { int nr_cells = num_size_; int cell_start = _PEN * nr_cells / 64; int cell_end = (_PEN + 1) * nr_cells / 64;
printf("Slave %d says: run! Start: %d, end: %d.\n", _PEN, cell_start, cell_end); task.Initialize(); for (int index = cell_start; index != cell_end; index++) { task.Self(index); } task.Finalize(); }
[[gnu::kernel]] void TimeIntegration::calculate_plus_test() { #ifdef __sw_slave__ swsph::CalcAccBf cal(arr_); for_each_compute(cal); #else #endif }
[[gnu::kernel]] void TimeIntegration::calculate_minus_test() { #ifdef __sw_slave__ swsph::CalcAccMinus cal(arr_); for_each_compute(cal); #else #endif }
[[gnu::host]] void TimeIntegration::run() { set_num_size(VECTOR_LENGTH); arr_.resize(num_size_, 0); calculate_plus_test(); athread_join(); for (int i = 0; i < VECTOR_LENGTH; i++) { printf("%d", arr_[i]); } printf("\n"); calculate_minus_test(); athread_join(); for (int i = 0; i < VECTOR_LENGTH; i++) { printf("%d", arr_[i]); } printf("\n"); }
int main() { athread_init(); TimeIntegration tm; tm.run(); }
|
函数前面使用[[gnu::kernel]]
修饰一下,编译器自动识别并分别编译。这样可以实现混合编译,不需要单独写一个从核.c文件。更重要的是支持C++,这个确实方便了很多。
编译选项
1 2 3 4
| fake_path/swg++ -mhybrid-coding task_acc_bf.cc -c -mslave -o build/task-slave.o fake_path/swg++ -mhybrid-coding main.cc -c -o build/host.o fake_path/swg++ -mhybrid-coding main.cc -c -mslave -o build/slave.o -msimd fake_path/swg++ -mhybrid build/task.o build/host.o build/slave.o build/task-slave.o -o build/for-each
|
总结 & Reference
本次是对于混合编译的初体验,对于lambda表达式的传递、对象传递等很多内容还需要继续学习体验,后续再更。
Reference: 神威混合编译手册。