Más contenido relacionado
La actualidad más candente (20)
Similar a PEZY-SC programming overview (20)
PEZY-SC programming overview
- 5. PEZY-SC2アーキテクチャ
PEZY-Confidential 5PEZY-SC2 (2,048PE)
Prefecture (256PE) City (16PE) Village (4PE) PE
Program Counter × 8
L1 Instruction Cache
64bit × 512w (4KB)
ALU
4FP ops/cycle
Register File
32bit × 512w (2KB)
Local Storage
32bit × 5120w (20KB)
PE
PE
L1 Data Cache
2KB
PE
PE
Village
(4PE)
L2 Data Cache
64KB
L2 Instruction Cache
32KB
Village
(4PE)
Village
(4PE)
Village
(4PE)
Special Function UnitCity
(16PE)
City
(16PE)
City
(16PE)
City
(16PE)
City
(16PE)
City
(16PE)
City
(16PE)
City
(16PE)
City
(16PE)
City
(16PE)
City
(16PE)
City
(16PE)
City
(16PE)
City
(16PE)
City
(16PE)
City
(16PE)
Prefecture (256PE) Prefecture (256PE)
LLC
2560KB
LLC
2560KB
LLC
2560KB
LLC
2560KB
Prefecture (256PE) Prefecture (256PE)
LLC
2560KB
LLC
2560KB
LLC
2560KB
LLC
2560KB
TCI DRAM 512GB/s
TCI DRAM 512GB/s
Prefecture (256PE) Prefecture (256PE)
LLC
2560KB
LLC
2560KB
LLC
2560KB
LLC
2560KB
Prefecture (256PE) Prefecture (256PE)
LLC
2560KB
LLC
2560KB
LLC
2560KB
LLC
2560KB
TCI DRAM 512GB/s
TCI DRAM 512GB/s
MIPS MIPS MIPS
MIPS MIPS MIPS
DDR4 DIMM 25GB/s DDR4 DIMM 25GB/s
DDR4 DIMM 25GB/s DDR4 DIMM 25GB/s
PCIe Gen4 x8
PCIe Gen4 x8
PCIe Gen4 x8
PCIe Gen4 x8
- 6. PEZY-SC2諸元
6
PEZY-SC2(2017-)
Process 16nm Fin FET+
Core Freq.
Core 1GHz(Target) / 700MHz(Current ZS2.2)
Peripherals 66MHz
On-chip
Memory
Cache
L1:4MB(D), 8MB(I), L2:8MB(D), 4MB(I)
LLC:40MB
ScratchPad 40MB(20KB/PE)
IPs
CPU MIPS64 R6(P6600) 6Core
PCIe I/F PCIe Gen4 8Lane 4Port(64GB/s)
DRAM I/F DDR4-3200MHz 4port(100GB/s)
Num of PEs 2,048 MIMD / 1,984 MIMD(Current ZS2.2)
Peak Performance
16.4TFlops/HP@1GHz, 11.2TFlops/HP@700MHz
8.2TFlops/SP@1GHz, 5.6TFlops/SP@700MHz
4.1TFlops/DP@1GHz, 2.8TFlops/DP@700MHz
Power Consumption 200W(peak estimated), ~100W(measured)
- 10. CPUプログラム
通常のC/C++プログラム+OpenCLライクな制御用
レイヤ(PZCL) により実装する
cl.hppが使えるので、使用感としてはOpenCLを書い
ているのと変わらない
10
std::vector<cl::Platform> platforms;
cl::Platform::get(&platforms);
if (platforms.size() == 0) {
std::cout << "Platform size 0¥n";
return -1;
}
cl_context_properties properties[] =
{ CL_CONTEXT_PLATFORM, (cl_context_properties)(platforms[0])(), 0};
cl::Context context(CL_DEVICE_TYPE_DEFAULT, properties);
std::vector<cl::Device> devices = context.getInfo<CL_CONTEXT_DEVICES>();
const char* helloStr = "void pzc_hello(void) { }";
cl::Program::Sources source(1, std::make_pair(helloStr,strlen(helloStr)));
cl::Program program_ = cl::Program(context, source);
program_.build(devices);
cl::Kernel kernel(program_, "hello", &err);
cl::Event event;
cl::CommandQueue queue(context, devices[0], 0, &err);
queue.enqueueNDRangeKernel(kernel, cl::NullRange, cl::NDRange(128), cl::NullRange, NULL, &event);
event.wait();
- 14. カーネルプログラム
以下のようなPEZY-SC制御に必要なBuiltIn-APIがある
sync_L1 (L1キャッシュにアクセスする単位でのスレッド同期)
sync_L2 (L2キャッシュにアクセスする単位でのスレッド同期)
sync (グローバルなスレッド同期)
flush_L1 (L1キャッシュのフラッシュ)
flush_L2 (L2キャッシュのフラッシュ)
flush (グローバルなフラッシュ)
get_pid (PE ID取得)
get_tid (PE内スレッドID取得)
chgthread (PE内スレッドの表裏切り替え)
14
- 17. カーネルプログラム(例)
void pzc_foo(float* a, …)
{
PE ID取得(get_pid)
PE内スレッドID取得(get_tid)
自スレッドに割り当てられた処理の実行
出力バッファフラッシュ(flush)
}
Confidential 17
OpenCLとの違い
__kernel void foo(__global float* a, …)
{
global_id取得(get_global_id(0))
自スレッドに割り当てられた処理の実行
}
- 18. カーネルプログラム(例)
void pzc_foo(float* a, …)
{
PE ID取得(get_pid)
PE内スレッドID取得(get_tid)
自スレッドに割り当てられた処理の実行
出力バッファフラッシュ(flush)
}
Confidential 18
OpenCLとの違い
__kernel void foo(__global float* a, …)
{
global_id取得(get_global_id(0))
自スレッドに割り当てられた処理の実行
}
修飾子ではなく、
”pzc_” prefix
__global修飾子は
必要ない
バッファのキャッシュ書き
戻しはソフトウェア責任
- 21. pzcAdd
vecAdd
CPU実装
21
void vecAdd(size_t num, double* dst, const double* src0, const double* src1)
{
for(size_t i = 0; i < count; i++) {
dst[i] = src0[i] + src1[i];
}
}
dst src0 src1
= +
- 23. void pzc_add(size_t num,
double* dst,
const double* src0,
const double* src1)
{
size_t pid = get_pid();
size_t tid = get_tid();
size_t gid = pid * get_maxtid() + tid;
const size_t GLOBAL_WORK_SIZE = get_maxtid() *
get_maxpid();
for (size_t i = gid; i < num; i += GLOBAL_WORK_SIZE) {
double s0 = src0[i];
double s1 = src1[i];
chgthread();
dst[i] = s0 + s1;
}
flush();
}
23
• カーネルプログラム
• 加算するコードをPEZY-SCのカーネルコードとして実装
• pzc_add()が指定されたスレッド数分実行される。
• tid, pidにユニークなIDが割り当てられるので、各スレッドはそれぞれの
indexを計算する。
pzcAdd
dst[0] = src0[0] + src1[0]
dst[1] = src0[1] + src1[1]
dst[2] = src0[2] + src1[2]
‥‥‥
スレッド0
スレッド1
スレッド2
- 24. pzcAdd
各スレッドの動作
24
void pzc_add(size_t num,
double* dst,
const double* src0,
const double* src1)
{
size_t pid = get_pid();
size_t tid = get_tid();
size_t gid = pid * get_maxtid() + tid;
const size_t GLOBAL_WORK_SIZE =
get_maxtid() * get_maxpid();
for (size_t i = gid; i < num; i += GLOBAL_WORK_SIZE) {
double s0 = src0[i];
double s1 = src1[i];
chgthread();
dst[i] = s0 + s1;
}
flush();
}
- 26. pzcAdd
CPUプログラム
ハンドリング処理(1/5)
Platformの取得
Platformに紐づくデバイスの取得
使用するデバイスの指定
26
// Get Platform
std::vector<cl::Platform> platforms;
cl::Platform::get(&platforms);
const auto& Platform = platforms[0];
// Get devices
std::vector<cl::Device> devices;
Platform.getDevices(CL_DEVICE_TYPE_DEFAULT, &devices);
// Use first device.
const auto& device = devices[0];
- 27. pzcAdd
CPUプログラム
ハンドリング処理(2/5)
デバイスとの間にContextを作成
Contextに対して、コマンドを送り込むCommandQueue
を作成
デバイスに紐づくカーネルのバイナリを読み込む
カーネル名をもとにKernelオブジェクトを作成
27
// Create Context.
auto context = cl::Context(device);
// Create CommandQueue.
auto command_queue = cl::CommandQueue(context, device, 0);
// Create Program.
// Load compiled binary file and create cl::Program object.
auto program = createProgram(context, device, "kernel/kernel.pz");
// Create Kernel.
// Give kernel name without pzc_ prefix.
auto kernel = cl::Kernel(program, "add");
- 28. pzcAdd
CPUプログラム
ハンドリング処理(3/5)
デバイス用のメモリ領域を確保
デバイス用入力メモリ領域に対してホスト用入力メモリ領
域のデータを転送
デバイス用出力メモリ領域を初期化
28
// Create Buffers.
auto device_src0 = cl::Buffer(context, CL_MEM_READ_WRITE, sizeof(double) * num);
auto device_src1 = cl::Buffer(context, CL_MEM_READ_WRITE, sizeof(double) * num);
auto device_dst = cl::Buffer(context, CL_MEM_READ_WRITE, sizeof(double) * num);
// Send src.
command_queue.enqueueWriteBuffer(device_src0, true, 0, sizeof(double) * num,
&src0[0]);
command_queue.enqueueWriteBuffer(device_src1, true, 0, sizeof(double) * num,
&src1[0]);
// Clear dst.
cl::Event write_event;
command_queue.enqueueFillBuffer(device_dst, 0, 0, sizeof(double) * num, nullptr,
&write_event);
write_event.wait();
- 29. pzcAdd
CPUプログラム
ハンドリング処理(4/5)
カーネルに対して引数をセット
起動するワークアイテム数を特定する
カーネルを起動
カーネルの終了を待つ
29
// Set kernel args.
kernel.setArg(0, num);
kernel.setArg(1, device_dst);
kernel.setArg(2, device_src0);
kernel.setArg(3, device_src1);
// Get workitem size.
// sc1-64: 8192 (1024 PEs * 8 threads)
// sc2 : 15782 (1984 PEs * 8 threads)
size_t global_work_size = ...
// Run device kernel.
cl::Event event;
command_queue.enqueueNDRangeKernel(kernel, cl::NullRange,
cl::NDRange(global_work_size), cl::NullRange, nullptr, &event);
// Waiting device completion.
event.wait();
PEZY-SC2の制約
ワークアイテムは128の倍数とする
物理スレッド数(15872)を超えたスレッドをワークアイテ
ムとして指定しても動作はするが、その場合はn回のカーネ
ル起動になる
- 30. pzcAdd
ワークアイテム数
15872?
現在のPEZY-SC2の仕様
歩留まり向上のため、124 Cityが動くものを使用
124 City * 16PE * 8Thread = 15872をSC2では基準とす
る
30
$ ./MultiDevice
-----------------------------------------------
Program: ./MultiDevice
Use 8 device(s)
Work size = 16128
Work size = 16384
Work size = 16000
Work size = 16384
Work size = 16384
Work size = 16000
Work size = 16256
Work size = 16128
- 31. pzcAdd
CPUプログラム
ハンドリング処理(5/5)
デバイス用出力メモリ領域からホスト用出力メモリ領域に
データを転送
CommandQueueに残ったコマンドの終了を待つ
31
// Get dst.
command_queue.enqueueReadBuffer(device_dst, true, 0, sizeof(double) * num,
&dst[0]);
// Finish all commands.
command_queue.flush();
command_queue.finish();
- 35. プロファイルの取り方
SDKのExtensionを使用
使い方
関数を確認する
PZSDKのドキュメントのURL入れる
Extentionのアドレスを取得する
これはOpenCLのやり方と同じ
35
// Please refer to the pzcl_ext.h or Runtime Extensions section in pzsdk document.
pfnPezyExtSetProfile clExtSetProfile = nullptr;
pfnPezyExtGetProfilePEStatistics clExtGetProfilePEStatistics = nullptr;
pfnPezyExtGetProfilePE clExtGetProfilePE = nullptr;
pfnPezyExtGetProfileCacheStatistics clExtGetProfileCacheStatistics = nullptr;
pfnPezyExtGetProfileCache clExtGetProfileCache = nullptr;
clExtSetProfile = (pfnPezyExtSetProfile)clGetExtensionFunctionAddress("pezy_set_profile");
clExtGetProfilePEStatistics =
(pfnPezyExtGetProfilePEStatistics)clGetExtensionFunctionAddress("pezy_get_profile_pe_statistics");
clExtGetProfilePE =
(pfnPezyExtGetProfilePE)clGetExtensionFunctionAddress("pezy_get_profile_pe");
clExtGetProfileCacheStatistics =
(pfnPezyExtGetProfileCacheStatistics)clGetExtensionFunctionAddress("pezy_get_profile_cache_statistics");
clExtGetProfileCache =
(pfnPezyExtGetProfileCache)clGetExtensionFunctionAddress("pezy_get_profile_cache");
- 36. プロファイルの取り方
SDKのExtensionを使用
使い方
pfnPezyExtSetProfile
プロファイルを有効/無効にする
pfnPezyExtGetProfilePEStatistics
PE全体のプロファイル情報を取得する
pfnPezyExtGetProfilePE
PEそれぞれのプロファイル情報を取得する
pfnPezyExtGetProfileCacheStatistics
キャッシュ(L1, L2) 全体のプロファイル情報を取得する
pfnPezyExtGetProfileCache
キャッシュ(L1, L2) それぞれのプロファイル情報を取得す
る
36
- 37. プロファイルの取り方
SDKのExtensionを使用
sampleの結果
37
$ ./ext_profile 65536
num 65536
Use device : PEZY-SC2
workitem : 15872
***** PE profile statistics *****
elapse_ns : 9741 [ns]
efficiency : 11.129 [%]
***** First city PE profile *****
PE 0 (run, stall, wait) : ( 6819, 2745, 3222) [cycle]
PE 1 (run, stall, wait) : ( 6819, 2685, 3282) [cycle]
…
PE 15 (run, stall, wait) : ( 6819, 3069, 2898) [cycle]
***** L1 cache profile statistics *****
read hit rate : 89.264 [%]
write hit rate : 87.500 [%]
***** First city L1 cache profile *****
Vill 0 read (request, hit) : ( 184, 164) [count]
Vill 0 write (request, hit) : ( 40, 35) [count]
…
Vill 3 read (request, hit) : ( 184, 164) [count]
Vill 3 write (request, hit) : ( 40, 35) [count]
***** L2 cache profile statistics *****
read hit rate : 68.185 [%]
write hit rate : 75.000 [%]
***** First city L2 cache profile *****
City 0 read (request, hit) : ( 336, 231) [count]
City 0 write (request, hit) : ( 80, 60) [count]
PASS
- 38. プロファイルの取り方
パフォーマンスカウンタを読む
参考になりそうなsample: なし(!?)
以下のようなコードで読める
Run = 0x18, stall = 0x1a, wait = 0x1e
38
using perf_counter_t = uint64_t;
template<int reg_idx>
perf_counter_t
read_perf_gpr(void)
{
while (1) {
uint32_t hi0 = __builtin_pz_read_gpr(reg_idx + 1);
uint32_t lo0 = __builtin_pz_read_gpr(reg_idx + 0);
uint32_t hi1 = __builtin_pz_read_gpr(reg_idx + 1);
if (hi1 == hi0) {
return (((uint64_t)hi0)<<32) | lo0;
}
}
return 0;
}
- 44. PEZY-SC2アーキテクチャ(再掲)
PEZY-Confidential 44PEZY-SC2 (2,048PE)
Prefecture (256PE) City (16PE) Village (4PE) PE
Program Counter × 8
L1 Instruction Cache
64bit × 512w (4KB)
ALU
4FP ops/cycle
Register File
32bit × 512w (2KB)
Local Storage
32bit × 5120w (20KB)
PE
PE
L1 Data Cache
2KB
PE
PE
Village
(4PE)
L2 Data Cache
64KB
L2 Instruction Cache
32KB
Village
(4PE)
Village
(4PE)
Village
(4PE)
Special Function UnitCity
(16PE)
City
(16PE)
City
(16PE)
City
(16PE)
City
(16PE)
City
(16PE)
City
(16PE)
City
(16PE)
City
(16PE)
City
(16PE)
City
(16PE)
City
(16PE)
City
(16PE)
City
(16PE)
City
(16PE)
City
(16PE)
Prefecture (256PE) Prefecture (256PE)
LLC
2560KB
LLC
2560KB
LLC
2560KB
LLC
2560KB
Prefecture (256PE) Prefecture (256PE)
LLC
2560KB
LLC
2560KB
LLC
2560KB
LLC
2560KB
TCI DRAM 512GB/s
TCI DRAM 512GB/s
Prefecture (256PE) Prefecture (256PE)
LLC
2560KB
LLC
2560KB
LLC
2560KB
LLC
2560KB
Prefecture (256PE) Prefecture (256PE)
LLC
2560KB
LLC
2560KB
LLC
2560KB
LLC
2560KB
TCI DRAM 512GB/s
TCI DRAM 512GB/s
MIPS MIPS MIPS
MIPS MIPS MIPS
DDR4 DIMM 25GB/s DDR4 DIMM 25GB/s
DDR4 DIMM 25GB/s DDR4 DIMM 25GB/s
PCIe Gen4 x8
PCIe Gen4 x8
PCIe Gen4 x8
PCIe Gen4 x8
- 45. PEZY-SC2アーキテクチャ(再掲)
PEZY-Confidential 45PEZY-SC2 (2,048PE)
Prefecture (256PE) City (16PE) Village (4PE) PE
Program Counter × 8
L1 Instruction Cache
64bit × 512w (4KB)
ALU
4FP ops/cycle
Register File
32bit × 512w (2KB)
Local Storage
32bit × 5120w (20KB)
PE
PE
L1 Data Cache
2KB
PE
PE
Village
(4PE)
L2 Data Cache
64KB
L2 Instruction Cache
32KB
Village
(4PE)
Village
(4PE)
Village
(4PE)
Special Function UnitCity
(16PE)
City
(16PE)
City
(16PE)
City
(16PE)
City
(16PE)
City
(16PE)
City
(16PE)
City
(16PE)
City
(16PE)
City
(16PE)
City
(16PE)
City
(16PE)
City
(16PE)
City
(16PE)
City
(16PE)
City
(16PE)
Prefecture (256PE) Prefecture (256PE)
LLC
2560KB
LLC
2560KB
LLC
2560KB
LLC
2560KB
Prefecture (256PE) Prefecture (256PE)
LLC
2560KB
LLC
2560KB
LLC
2560KB
LLC
2560KB
TCI DRAM 512GB/s
TCI DRAM 512GB/s
Prefecture (256PE) Prefecture (256PE)
LLC
2560KB
LLC
2560KB
LLC
2560KB
LLC
2560KB
Prefecture (256PE) Prefecture (256PE)
LLC
2560KB
LLC
2560KB
LLC
2560KB
LLC
2560KB
TCI DRAM 512GB/s
TCI DRAM 512GB/s
MIPS MIPS MIPS
MIPS MIPS MIPS
DDR4 DIMM 25GB/s DDR4 DIMM 25GB/s
DDR4 DIMM 25GB/s DDR4 DIMM 25GB/s
PCIe Gen4 x8
PCIe Gen4 x8
PCIe Gen4 x8
PCIe Gen4 x8
11.2 TB/s
11.2 TB/s
1.4 TB/s
76 GB/s
- 46. PEZY-SC2最適化入門
ローカルメモリを使う
参考になるsample: 2_Advanced/pzcAdd_local
ローカルメモリ?
46
PE
Program Counter × 8
L1 Instruction Cache
64bit × 512w (4KB)
ALU
4FP ops/cycle
Register File
32bit × 512w (2KB)
Local Storage
32bit × 5120w (20KB)
PE
L1 Data Cache
2KB
各PEに20KBある
普段はスタックとして使用
スタックは2.5KB/Thread
スレッド7用スタック領域(2.5KB)
スレッド6用スタック領域(2.5KB)
スレッド5用スタック領域(2.5KB)
スレッド4用スタック領域(2.5KB)
スレッド3用スタック領域(2.5KB)
スレッド2用スタック領域(2.5KB)
スレッド1用スタック領域(2.5KB)
スレッド0用スタック領域(2.5KB)
20KB
0x0000
0x4fff
- 47. PEZY-SC2最適化入門
ローカルメモリを使う
スタックのサイズは任意に変更可能
1KB/Threadにすると、12KBがユーザー利用可能領域にな
る
ユーザー利用可能領域は1PE(=8スレッド)で共有
47
スレッド7用スタック領域(2.5KB)
スレッド6用スタック領域(2.5KB)
スレッド5用スタック領域(2.5KB)
スレッド4用スタック領域(2.5KB)
スレッド3用スタック領域(2.5KB)
スレッド2用スタック領域(2.5KB)
スレッド1用スタック領域(2.5KB)
スレッド0用スタック領域(2.5KB)
20KB
0x0000
0x4fff
スレッド7用スタック領域(1KB)
スレッド6用スタック領域(1KB)
スレッド5用スタック領域(1KB)
スレッド4用スタック領域(1KB)
スレッド3用スタック領域(1KB)
スレッド2用スタック領域(1KB)
スレッド1用スタック領域(1KB)
スレッド0用スタック領域(1KB)
8KB
0x0000
0x2000
ユーザ利用可能領域(12KB) 12KB
0x4fff
- 48. PEZY-SC2最適化入門
ローカルメモリを使う
使い方
SDKのExtensionを使う
Host
dev
48
// Create Kernel.
// Give kernel name without pzc_ prefix.
auto kernel = cl::Kernel(program, "addWithLocal");
// Get stack size modify function.
typedef CL_API_ENTRY cl_int(CL_API_CALL *
pfnPezyExtSetPerThreadStackSize)(cl_kernel kernel, size_t size);
const auto clExtSetPerThreadStackSize =
reinterpret_cast<pfnPezyExtSetPerThreadStackSize>(clGetExtensionFunctionAddress("
pezy_set_per_thread_stack_size"));
if (clExtSetPerThreadStackSize == nullptr) {
throw "pezy_set_per_thread_stack_size not found";
}
// Set stack size each thread.
size_t stack_size_per_thread = 1024;
clExtSetPerThreadStackSize(kernel(), stack_size_per_thread);
void* local_mem_top_addr = (void*)get_local_mem_addr();
- 51. PEZY-SC2最適化入門
スレッドを切り替える
スレッドを切り替えないとどうなるのか?
flushに到達して初めてスレッドが切り替わる
ストールは消せないし、キャッシュアクセスも良くない
51
void pzc_add(size_t num,
double* dst,
const double* src0,
const double* src1)
{
size_t pid = get_pid();
size_t tid = get_tid();
size_t gid = pid * get_maxtid() + tid;
const size_t GLOBAL_WORK_SIZE = get_maxtid() *
get_maxpid();
for (size_t i = gid; i < num; i += GLOBAL_WORK_SIZE) {
double s0 = src0[i];
double s1 = src1[i];
dst[i] = s0 + s1;
}
flush();
}
t0 t4
flush
memory
request
- 52. PEZY-SC2最適化入門
スレッドを切り替える
スレッドを切り替えるとどうなるのか
メモリアクセスしたときのストールを、別のスレッドに切
り替えることにより隠せる
キャッシュにも当たりやすい
52
void pzc_add(size_t num,
double* dst,
const double* src0,
const double* src1)
{
size_t pid = get_pid();
size_t tid = get_tid();
size_t gid = pid * get_maxtid() + tid;
const size_t GLOBAL_WORK_SIZE = get_maxtid() *
get_maxpid();
for (size_t i = gid; i < num; i += GLOBAL_WORK_SIZE) {
double s0 = src0[i];
double s1 = src1[i];
dst[i] = s0 + s1;
chgthread();
}
flush();
}
t0 t4
memory
requeststall
chgthread
- 53. PEZY-SC2最適化入門
アクセスのタイミングをそろえる
メモリアクセスのタイミングがバラバラ
53
void pzc_add(size_t num,
double* dst,
const double* src0,
const double* src1)
{
size_t pid = get_pid();
size_t tid = get_tid();
size_t gid = pid * get_maxtid() + tid;
const size_t GLOBAL_WORK_SIZE = get_maxtid() *
get_maxpid();
for (size_t i = gid; i < num; i += GLOBAL_WORK_SIZE) {
double s0 = src0[i];
double s1 = src1[i];
chgthread();
dst[i] = s0 + s1;
}
flush();
}
t0 t1 … t7
memory
request
- 54. PEZY-SC2最適化入門
アクセスのタイミングをそろえる
メモリアクセスのタイミングが揃ったことにより性能が向
上する”場合がある”
Syncのコストと比較してどうなるかは要検討
54
void pzc_add(size_t num,
double* dst,
const double* src0,
const double* src1)
{
size_t pid = get_pid();
size_t tid = get_tid();
size_t gid = pid * get_maxtid() + tid;
const size_t GLOBAL_WORK_SIZE = get_maxtid() *
get_maxpid();
for (size_t i = gid; i < num; i += GLOBAL_WORK_SIZE) {
sync_L2();
double s0 = src0[i];
double s1 = src1[i];
chgthread();
dst[i] = s0 + s1;
}
flush();
}
t0 t1 … t7
memory
request
sync