Optimization for CPU cache
This commit is contained in:
@@ -1,5 +1,5 @@
|
||||
// reedsolomon.c
|
||||
// Copyright : 2023-05-29 Yutaka Sawada
|
||||
// Copyright : 2023-09-23 Yutaka Sawada
|
||||
// License : GPL
|
||||
|
||||
#ifndef _UNICODE
|
||||
@@ -38,6 +38,13 @@
|
||||
#define GPU_SOURCE_COUNT_LIMIT 256
|
||||
#define GPU_PARITY_COUNT_LIMIT 32
|
||||
|
||||
/*
|
||||
#define GPU_DATA_LIMIT 1
|
||||
#define GPU_BLOCK_SIZE_LIMIT 32768
|
||||
#define GPU_SOURCE_COUNT_LIMIT 16
|
||||
#define GPU_PARITY_COUNT_LIMIT 4
|
||||
*/
|
||||
|
||||
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
|
||||
|
||||
// chunk がキャッシュに収まるようにすれば速くなる! (Cache Blocking という最適化手法)
|
||||
@@ -46,7 +53,7 @@ int try_cache_blocking(int unit_size)
|
||||
int limit_size, chunk_count, chunk_size, cache_line_diff;
|
||||
|
||||
// CPUキャッシュをどのくらいまで使うか
|
||||
limit_size = cpu_flag & 0x7FFF8000; // 最低でも 32KB になる
|
||||
limit_size = cpu_flag & 0x7FFF0000; // 最低でも 64KB になる
|
||||
if (limit_size == 0) // キャッシュ・サイズを取得できなかった場合は最適化しない
|
||||
return unit_size;
|
||||
|
||||
@@ -160,7 +167,6 @@ unsigned int get_io_size(
|
||||
// 何ブロックまとめてファイルから読み込むかを空きメモリー量から計算する
|
||||
int read_block_num(
|
||||
int keep_num, // 保持するパリティ・ブロック数
|
||||
int add_num, // 余裕を見るブロック数
|
||||
size_t trial_alloc, // 確保できるか確認するのか
|
||||
int alloc_unit) // メモリー単位の境界 (sse_unit か MEM_UNIT)
|
||||
{
|
||||
@@ -177,7 +183,7 @@ int read_block_num(
|
||||
|
||||
if (trial_alloc){
|
||||
__int64 possible_size;
|
||||
possible_size = (__int64)unit_size * (source_num + keep_num + add_num);
|
||||
possible_size = (__int64)unit_size * (source_num + keep_num);
|
||||
#ifndef _WIN64 // 32-bit 版なら
|
||||
if (possible_size > MAX_MEM_SIZE) // 確保する最大サイズを 2GB までにする
|
||||
possible_size = MAX_MEM_SIZE;
|
||||
@@ -191,13 +197,13 @@ int read_block_num(
|
||||
}
|
||||
mem_size = get_mem_size(trial_alloc) / unit_size; // 何個分確保できるか
|
||||
|
||||
if (mem_size >= (size_t)(source_num + keep_num + add_num)){ // 最大個数より多い
|
||||
if (mem_size >= (size_t)(source_num + keep_num)){ // 最大個数より多い
|
||||
buf_num = source_num;
|
||||
} else if ((int)mem_size < read_min + keep_num + add_num){ // 少なすぎる
|
||||
} else if ((int)mem_size < read_min + keep_num){ // 少なすぎる
|
||||
buf_num = 0; // メモリー不足の印
|
||||
} else { // ソース・ブロック個数を等分割する
|
||||
int split_num;
|
||||
buf_num = (int)mem_size - (keep_num + add_num);
|
||||
buf_num = (int)mem_size - keep_num;
|
||||
split_num = (source_num + buf_num - 1) / buf_num; // 何回に別けて読み込むか
|
||||
buf_num = (source_num + split_num - 1) / split_num;
|
||||
}
|
||||
@@ -263,7 +269,7 @@ static int invert_matrix_st(unsigned short *mat,
|
||||
|
||||
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
|
||||
// マルチ・プロセッサー対応
|
||||
|
||||
/*
|
||||
typedef struct { // RS threading control struct
|
||||
unsigned short *mat; // 行列
|
||||
int cols; // 横行の長さ
|
||||
@@ -308,8 +314,57 @@ static DWORD WINAPI thread_func(LPVOID lpParameter)
|
||||
CloseHandle(th->end);
|
||||
return 0;
|
||||
}
|
||||
*/
|
||||
typedef struct { // Maxtrix Inversion threading control struct
|
||||
unsigned short *mat; // 行列
|
||||
int cols; // 横行の長さ
|
||||
volatile int start; // 掛ける行の先頭位置
|
||||
volatile int pivot; // 倍率となる値の位置
|
||||
volatile int skip; // とばす行
|
||||
volatile int now; // 消去する行
|
||||
HANDLE run;
|
||||
HANDLE end;
|
||||
} INV_TH;
|
||||
|
||||
// サブ・スレッド
|
||||
static DWORD WINAPI thread_func(LPVOID lpParameter)
|
||||
{
|
||||
unsigned short *mat;
|
||||
int j, cols, row_start2, factor;
|
||||
HANDLE hRun, hEnd;
|
||||
INV_TH *th;
|
||||
|
||||
th = (INV_TH *)lpParameter;
|
||||
mat = th->mat;
|
||||
cols = th->cols;
|
||||
hRun = th->run;
|
||||
hEnd = th->end;
|
||||
SetEvent(hEnd); // 設定完了を通知する
|
||||
|
||||
WaitForSingleObject(hRun, INFINITE); // 計算開始の合図を待つ
|
||||
while (th->skip >= 0){
|
||||
while ((j = InterlockedDecrement(&(th->now))) >= 0){ // j = --th_now
|
||||
if (j == th->skip)
|
||||
continue;
|
||||
row_start2 = cols * j; // その行の開始位置
|
||||
factor = mat[row_start2 + th->pivot]; // j 行の pivot 列の値
|
||||
mat[row_start2 + th->pivot] = 0; // これが行列を一個で済ます手
|
||||
// 先の計算により、i 行の pivot 列の値は必ず 1なので、この factor が倍率になる
|
||||
galois_region_multiply(mat + th->start, mat + row_start2, cols, factor);
|
||||
}
|
||||
//_mm_sfence(); // メモリーへの書き込みを完了する
|
||||
SetEvent(hEnd); // 計算終了を通知する
|
||||
WaitForSingleObject(hRun, INFINITE); // 計算開始の合図を待つ
|
||||
}
|
||||
|
||||
// 終了処理
|
||||
CloseHandle(hRun);
|
||||
CloseHandle(hEnd);
|
||||
return 0;
|
||||
}
|
||||
|
||||
// マルチ・スレッドで逆行列を計算する (利用するパリティ・ブロックの所だけ)
|
||||
/*
|
||||
static int invert_matrix_mt(unsigned short *mat,
|
||||
int rows, // 横行の数、行列の縦サイズ、失われたソース・ブロックの数 = 利用するパリティ・ブロック数
|
||||
int cols, // 縦列の数、行列の横サイズ、本来のソース・ブロック数
|
||||
@@ -411,6 +466,130 @@ static int invert_matrix_mt(unsigned short *mat,
|
||||
CloseHandle(th->h);
|
||||
return 0;
|
||||
}
|
||||
*/
|
||||
|
||||
static int invert_matrix_mt(unsigned short *mat,
|
||||
int rows, // 横行の数、行列の縦サイズ、失われたソース・ブロックの数 = 利用するパリティ・ブロック数
|
||||
int cols, // 縦列の数、行列の横サイズ、本来のソース・ブロック数
|
||||
source_ctx_r *s_blk) // 各ソース・ブロックの情報
|
||||
{
|
||||
int err = 0, j, row_start2, factor, sub_num;
|
||||
unsigned int time_last = GetTickCount();
|
||||
HANDLE hSub[MAX_CPU / 2], hRun[MAX_CPU / 2], hEnd[MAX_CPU / 2];
|
||||
INV_TH th[1];
|
||||
|
||||
memset(hSub, 0, sizeof(HANDLE) * (MAX_CPU / 2));
|
||||
memset(th, 0, sizeof(INV_TH));
|
||||
|
||||
// サブ・スレッドの数は平方根(切り上げ)にする
|
||||
sub_num = 1;
|
||||
j = 2;
|
||||
while (j < cpu_num){ // 1~2=1, 3~4=2, 5~8=3, 9~16=4, 17~32=5
|
||||
sub_num++;
|
||||
j *= 2;
|
||||
}
|
||||
if (sub_num > rows - 2)
|
||||
sub_num = rows - 2; // 多過ぎても意味ないので制限する
|
||||
#ifdef TIMER
|
||||
// 使うスレッド数は、メイン・スレッドの分も含めるので 1個増える
|
||||
printf("\nMaxtrix Inversion with %d threads\n", sub_num + 1);
|
||||
#endif
|
||||
|
||||
// サブ・スレッドを起動する
|
||||
th->mat = mat;
|
||||
th->cols = cols;
|
||||
for (j = 0; j < sub_num; j++){ // サブ・スレッドごとに
|
||||
// イベントを作成する
|
||||
hRun[j] = CreateEvent(NULL, FALSE, FALSE, NULL); // 両方とも Auto Reset にする
|
||||
if (hRun[j] == NULL){
|
||||
print_win32_err();
|
||||
printf("error, inv-thread\n");
|
||||
err = 1;
|
||||
goto error_end;
|
||||
}
|
||||
hEnd[j] = CreateEvent(NULL, FALSE, FALSE, NULL);
|
||||
if (hEnd[j] == NULL){
|
||||
print_win32_err();
|
||||
CloseHandle(hRun[j]);
|
||||
printf("error, inv-thread\n");
|
||||
err = 1;
|
||||
goto error_end;
|
||||
}
|
||||
// サブ・スレッドを起動する
|
||||
th->run = hRun[j];
|
||||
th->end = hEnd[j];
|
||||
//_mm_sfence(); // メモリーへの書き込みを完了してからスレッドを起動する
|
||||
hSub[j] = (HANDLE)_beginthreadex(NULL, STACK_SIZE, thread_func, (LPVOID)th, 0, NULL);
|
||||
if (hSub[j] == NULL){
|
||||
print_win32_err();
|
||||
CloseHandle(hRun[j]);
|
||||
CloseHandle(hEnd[j]);
|
||||
printf("error, inv-thread\n");
|
||||
err = 1;
|
||||
goto error_end;
|
||||
}
|
||||
WaitForSingleObject(hEnd[j], INFINITE); // 設定終了の合図を待つ (リセットする)
|
||||
}
|
||||
|
||||
// Gaussian Elimination with 1 matrix
|
||||
th->pivot = 0;
|
||||
th->start = 0; // その行の開始位置
|
||||
for (th->skip = 0; th->skip < rows; th->skip++){
|
||||
// 経過表示
|
||||
if (GetTickCount() - time_last >= UPDATE_TIME){
|
||||
if (print_progress((th->skip * 1000) / rows)){
|
||||
err = 2;
|
||||
goto error_end;
|
||||
}
|
||||
time_last = GetTickCount();
|
||||
}
|
||||
|
||||
// その行 (パリティ・ブロック) がどのソース・ブロックの代用か
|
||||
while ((th->pivot < cols) && (s_blk[th->pivot].exist != 0))
|
||||
th->pivot++;
|
||||
|
||||
// Divide the row by element i,pivot
|
||||
factor = mat[th->start + th->pivot];
|
||||
if (factor > 1){
|
||||
mat[th->start + th->pivot] = 1; // これが行列を一個で済ます手
|
||||
galois_region_divide(mat + th->start, cols, factor);
|
||||
} else if (factor == 0){ // factor = 0 だと、その行列の逆行列を計算できない
|
||||
err = (0x00010000 | th->pivot); // どのソース・ブロックで問題が発生したのかを返す
|
||||
goto error_end;
|
||||
}
|
||||
|
||||
// 別の行の同じ pivot 列が 0以外なら、その値を 0にするために、
|
||||
// i 行を何倍かしたものを XOR する
|
||||
th->now = rows; // 初期値 + 1
|
||||
//_mm_sfence(); // メモリーへの書き込みを完了してからスレッドを再開する
|
||||
for (j = 0; j < sub_num; j++)
|
||||
SetEvent(hRun[j]); // サブ・スレッドに計算を開始させる
|
||||
while ((j = InterlockedDecrement(&(th->now))) >= 0){ // j = --th_now
|
||||
if (j == th->skip) // 同じ行はとばす
|
||||
continue;
|
||||
row_start2 = cols * j; // その行の開始位置
|
||||
factor = mat[row_start2 + th->pivot]; // j 行の pivot 列の値
|
||||
mat[row_start2 + th->pivot] = 0; // これが行列を一個で済ます手
|
||||
// 先の計算により、i 行の pivot 列の値は必ず 1なので、この factor が倍率になる
|
||||
galois_region_multiply(mat + th->start, mat + row_start2, cols, factor);
|
||||
}
|
||||
|
||||
WaitForMultipleObjects(sub_num, hEnd, TRUE, INFINITE); // サブ・スレッドの計算終了の合図を待つ
|
||||
th->start += cols;
|
||||
th->pivot++;
|
||||
}
|
||||
|
||||
error_end:
|
||||
InterlockedExchange(&(th->skip), -1); // 終了指示
|
||||
for (j = 0; j < sub_num; j++){
|
||||
if (hSub[j]){ // サブ・スレッドを終了させる
|
||||
SetEvent(hRun[j]);
|
||||
WaitForSingleObject(hSub[j], INFINITE);
|
||||
CloseHandle(hSub[j]);
|
||||
}
|
||||
}
|
||||
return err;
|
||||
}
|
||||
|
||||
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
|
||||
/*
|
||||
@@ -539,11 +718,9 @@ unsigned int time_total = GetTickCount();
|
||||
}
|
||||
|
||||
// パリティ計算用の行列演算の準備をする
|
||||
if (parity_num > source_num){
|
||||
len = sizeof(unsigned short) * (source_num + parity_num);
|
||||
} else {
|
||||
len = sizeof(unsigned short) * source_num * 2;
|
||||
}
|
||||
len = sizeof(unsigned short) * source_num;
|
||||
if (OpenCL_method != 0)
|
||||
len *= 2; // GPU の作業領域も確保しておく
|
||||
constant = malloc(len);
|
||||
if (constant == NULL){
|
||||
printf("malloc, %d\n", len);
|
||||
@@ -551,7 +728,11 @@ unsigned int time_total = GetTickCount();
|
||||
goto error_end;
|
||||
}
|
||||
#ifdef TIMER
|
||||
printf("\nmatrix size = %d.%d KB\n", len >> 10, (len >> 10) % 10);
|
||||
if (len & 0xFFFFF000){
|
||||
printf("\nmatrix size = %u KB\n", len >> 10);
|
||||
} else {
|
||||
printf("\nmatrix size = %u Bytes\n", len);
|
||||
}
|
||||
#endif
|
||||
// パリティ検査行列の基になる定数
|
||||
make_encode_constant(constant);
|
||||
@@ -623,11 +804,9 @@ unsigned int time_total = GetTickCount();
|
||||
}
|
||||
|
||||
// パリティ計算用の行列演算の準備をする
|
||||
if (parity_num > source_num){
|
||||
len = sizeof(unsigned short) * (source_num + parity_num);
|
||||
} else {
|
||||
len = sizeof(unsigned short) * source_num * 2;
|
||||
}
|
||||
len = sizeof(unsigned short) * source_num;
|
||||
if (OpenCL_method != 0)
|
||||
len *= 2; // GPU の作業領域も確保しておく
|
||||
constant = malloc(len);
|
||||
if (constant == NULL){
|
||||
printf("malloc, %d\n", len);
|
||||
@@ -635,7 +814,11 @@ unsigned int time_total = GetTickCount();
|
||||
goto error_end;
|
||||
}
|
||||
#ifdef TIMER
|
||||
printf("\nmatrix size = %d.%d KB\n", len >> 10, (len >> 10) % 10);
|
||||
if (len & 0xFFFFF000){
|
||||
printf("\nmatrix size = %u KB\n", len >> 10);
|
||||
} else {
|
||||
printf("\nmatrix size = %u Bytes\n", len);
|
||||
}
|
||||
#endif
|
||||
// パリティ検査行列の基になる定数
|
||||
make_encode_constant(constant);
|
||||
@@ -719,9 +902,11 @@ unsigned int time_matrix = 0, time_total = GetTickCount();
|
||||
}
|
||||
#ifdef TIMER
|
||||
if (len & 0xFFF00000){
|
||||
printf("\nmatrix size = %d.%d MB\n", len >> 20, (len >> 20) % 10);
|
||||
printf("\nmatrix size = %u MB\n", len >> 20);
|
||||
} else if (len & 0x000FF000){
|
||||
printf("\nmatrix size = %u KB\n", len >> 10);
|
||||
} else {
|
||||
printf("\nmatrix size = %d.%d KB\n", len >> 10, (len >> 10) % 10);
|
||||
printf("\nmatrix size = %u Bytes\n", len);
|
||||
}
|
||||
#endif
|
||||
// 何番目の消失ソース・ブロックがどのパリティで代替されるか
|
||||
@@ -783,7 +968,7 @@ time_matrix = GetTickCount() - time_matrix;
|
||||
if (memory_use & 16){
|
||||
err = -4; // SSD なら Read all 方式でブロックが断片化しても速い
|
||||
} else
|
||||
if (read_block_num(block_lost, 2, 0, MEM_UNIT) != 0){
|
||||
if (read_block_num(block_lost, 0, MEM_UNIT) != 0){
|
||||
err = -5; // HDD でメモリーが足りてるなら Read some 方式を使う
|
||||
} else {
|
||||
err = -4; // メモリー不足なら Read all 方式でブロックを断片化させる
|
||||
@@ -793,7 +978,7 @@ time_matrix = GetTickCount() - time_matrix;
|
||||
if (memory_use & 16){
|
||||
err = -2; // SSD なら Read all 方式でブロックが断片化しても速い
|
||||
} else
|
||||
if (read_block_num(block_lost, cpu_num - 1, 0, sse_unit) != 0){
|
||||
if (read_block_num(block_lost, 0, sse_unit) != 0){
|
||||
err = -3; // HDD でメモリーが足りてるなら Read some 方式を使う
|
||||
} else {
|
||||
err = -2; // メモリー不足なら Read all 方式でブロックを断片化させる
|
||||
|
||||
Reference in New Issue
Block a user