Optimization for CPU cache

This commit is contained in:
Yutaka Sawada
2023-09-24 21:26:09 +09:00
committed by GitHub
parent 3024186aa6
commit 54931fc0e7
18 changed files with 2241 additions and 1296 deletions

View File

@@ -1,4 +1,4 @@
[ par2j.exe - version 1.3.2.8 or later ]
[ par2j.exe - version 1.3.3.0 or later ]
Type "par2j.exe" to see version, test integrity, and show usage below.
@@ -359,19 +359,22 @@ the protected archive file is made in the directory.
/lc :
Set this, if you want to set number of using threads for Multi-Core CPU,
or want to disable extra feature. (SSE2 is always used.)
The format is "/lc#", # is from 1 to 11 as the number of using threads,
12 to use quarter number of physical Cores,
13 to use half of physical Cores,
14 to use 3/4 number of physical Cores,
15 to use the number of physical Cores (disable Hyper Threading),
or 15 to use one less number of physical Cores on CPU with 6 or more physical Cores.
Without this option (or /lc0),
it uses the number of physical Cores on CPU with 6 or more physical Cores,
or one more threads on CPU with Hyper Threading and 5 or less physical Cores.
The format is "/lc#" (# is from 1 to 32 as the number of using threads).
You may set additional combinations; +16 to disable SSSE3,
+128 to disable CLMUL, +256 to disable JIT, +512 to disable AVX2,
+32 or +64 (slower device) to enable GPU acceleration.
It's possible to set by rate as following. (It's /lc0 by default.)
251: It uses quarter number of physical Cores.
252: It uses half of physical Cores.
253: It uses 3/4 number of physical Cores.
254: It uses one less threads than number of physical Cores.
0: It uses the number of physical Cores.
255: It uses one more threads than number of physical Cores.
You may set additional combinations;
+1024 to disable CLMUL (and use old SSSE3 code),
+2048 to disable JIT (for SSE2),
+4096 to disable SSSE3,
+8192 to disable AVX2,
+256 or +512 (slower device) to enable GPU acceleration.
for example, /lc1 to use single Core, /lc45 to use half Cores and GPU

View File

@@ -1,5 +1,5 @@
// common2.c
// Copyright : 2023-03-14 Yutaka Sawada
// Copyright : 2023-09-23 Yutaka Sawada
// License : GPL
#ifndef _UNICODE
@@ -1849,8 +1849,9 @@ int sqrt32(int num)
int cpu_num = 1; // CPU/Core 個数が制限されてる場合は、上位に本来の数を置く
// /arch:SSE2, +1=SSSE3, +2=SSE4.1, +4=SSE4.2, +8=CLMUL, +16=AVX2, +128=JIT(SSE2), +256=Old
// 上位 16-bit = L2 cache サイズから計算した制限サイズ
unsigned int cpu_flag = 0;
unsigned int cpu_cache = 0; // 上位 16-bit = L2 cache * 2, 下位 16-bit = L3 cache
unsigned int cpu_cache = 0; // 上位 16-bit = L3 cache の制限サイズ, 下位 16-bit = 同時処理数
unsigned int memory_use = 0; // メモリー使用量 0=auto, 17 -> 1/8 7/8
static int count_bit(DWORD_PTR value)
@@ -1869,7 +1870,7 @@ static int count_bit(DWORD_PTR value)
void check_cpu(void)
{
int core_count = 0, use_count;
unsigned int CPUInfo[4];
unsigned int CPUInfo[4], limit_size = 0;
unsigned int returnLength, byteOffset;
DWORD_PTR ProcessAffinityMask, SystemAffinityMask; // 32-bit なら 4バイト、64-bit なら 8バイト整数
PSYSTEM_LOGICAL_PROCESSOR_INFORMATION buffer = NULL, ptr;
@@ -2006,42 +2007,52 @@ void check_cpu(void)
//printf("Number of available physical processor cores: %d\n", core_count);
if (cache3_size > 0){
//printf("L3 cache: %d KB (%d way)\n", cache3_size >> 10 , cache3_way);
cache3_size /= cache3_way; // set-associative のサイズにする
if (cache3_size < 131072)
cache3_size = 128 << 10; // 128 KB 以上にする
cpu_cache = cache3_size / cache3_way; // set-associative のサイズにする
if (cpu_cache < 131072)
cpu_cache = 128 << 10; // 128 KB 以上にする
}
if (cache2_size > 0){
//printf("L2 cache: %d KB (%d way)\n", cache2_size >> 10, cache2_way);
cache2_size /= cache2_way; // set-associative のサイズにする
if (cache2_size < 32768)
cache2_size = 32 << 10; // 32 KB 以上にする
//printf("Limit size of Cache Blocking: %d KB\n", cache2_size >> 10);
cpu_cache = cache2_size | (cache3_size >> 17);
limit_size = cache2_size / cache2_way; // set-associative のサイズにする
if (limit_size < 65536)
limit_size = 64 << 10; // 64 KB 以上にする
// 同時処理数を決める
if (cache2_way >= 16){
returnLength = cache2_way / 2; // L2 cache の分割数が多い場合は、その半分にする
} else {
returnLength = 0;
}
if (cache3_size > 0){ // L2 cache に対する L3 cache のサイズの倍率にする
byteOffset = cache3_size / cache2_size;
if (returnLength < byteOffset){
returnLength = byteOffset;
if (cache2_way >= cache3_way) // L2 cache の分割数が L3 cache 以上なら 1.5倍にする
returnLength += returnLength / 2;
}
}
cpu_cache |= returnLength & 0x1FFFF;
}
}
if (cpu_cache == 0) // キャッシュ・サイズが不明なら、128 KB にする
cpu_cache = 128 << 10;
if (limit_size == 0) // キャッシュ・サイズが不明なら、128 KB にする
limit_size = 128 << 10;
//printf("Limit size of Cache Blocking: %d KB\n", limit_size >> 10);
// cpu_flag の上位 16-bit にキャッシュの制限サイズを置く
cpu_flag |= limit_size & 0xFFFF0000; // 64 KB 未満は無視する
if (core_count == 0){ // 物理コア数が不明なら、論理コア数と同じにする
core_count = cpu_num;
use_count = cpu_num;
} else if (core_count < cpu_num){ // 物理コアが共有されてるなら
if (core_count >= 6){ // 6 コア以上ならそれ以上増やさない
use_count = core_count;
} else { // 2~5 コアなら 1個だけ増やす
use_count = core_count + 1;
}
} else if (core_count < cpu_num){ // 物理コアが共有されてるなら
use_count = core_count; // 物理コア数と同じにする
} else {
use_count = core_count;
use_count = cpu_num; // 論理コア数と同じにする
}
if (use_count > MAX_CPU) // 利用するコア数が実装上の制限を越えないようにする
use_count = MAX_CPU;
//printf("Core count: logical, physical, use = %d, %d, %d\n", cpu_num, core_count, use_count);
// 上位に論理コア数と物理コア数、下位に利用するコア数を配置する
cpu_num = (cpu_num << 24) | (core_count << 16) | use_count;
// cpu_flag の上位 17-bit にキャッシュの制限サイズを置く
cpu_flag |= cpu_cache & 0xFFFF8000; // 32 KB 未満は無視する
}
// OS が 32-bit か 64-bit かを調べる

View File

@@ -6,11 +6,11 @@ extern "C" {
#endif
#ifndef _WIN64 // 32-bit 版なら
#define MAX_CPU 8 // 32-bit 版は少なくしておく
#define MAX_CPU 16 // 32-bit 版は少なくしておく
#define MAX_MEM_SIZE 0x7F000000 // 確保するメモリー領域の最大値 2032MB
#define MAX_MEM_SIZE32 0x50000000 // 32-bit OS で確保するメモリー領域の最大値 1280MB
#else
#define MAX_CPU 16 // 最大 CPU/Core 個数 (スレッド本数)
#define MAX_CPU 32 // 最大 CPU/Core 個数 (スレッド本数)
#endif
#define MAX_LEN 1024 // ファイル名の最大文字数 (末尾のNULL文字も含む)

View File

@@ -1,5 +1,5 @@
// create.c
// Copyright : 2022-02-16 Yutaka Sawada
// Copyright : 2023-09-23 Yutaka Sawada
// License : GPL
#ifndef _UNICODE

File diff suppressed because it is too large Load Diff

View File

@@ -47,6 +47,15 @@ typedef void (* REGION_MULTIPLY) (
int factor); // Number to multiply by
REGION_MULTIPLY galois_align_multiply;
typedef void (* REGION_MULTIPLY2) (
unsigned char *src1, // Region to multiply
unsigned char *src2,
unsigned char *dst, // Products go here
unsigned int len, // Byte length
int factor1, // Number to multiply by
int factor2);
REGION_MULTIPLY2 galois_align_multiply2;
// 領域並び替え用の関数定義
typedef void (* REGION_ALTMAP) (unsigned char *data, unsigned int bsize);
REGION_ALTMAP galois_altmap_change;

View File

@@ -1,5 +1,5 @@
// lib_opencl.c
// Copyright : 2023-06-01 Yutaka Sawada
// Copyright : 2023-09-23 Yutaka Sawada
// License : GPL
#ifndef _WIN32_WINNT
@@ -72,11 +72,10 @@ typedef cl_int (CL_API_CALL *API_clEnqueueNDRangeKernel)(cl_command_queue, cl_ke
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
// グローバル変数
extern unsigned int cpu_flag, cpu_cache; // declared in common2.h
extern unsigned int cpu_flag; // declared in common2.h
extern int cpu_num;
#define MAX_DEVICE 3
#define MAX_GROUP_NUM 64
HMODULE hLibOpenCL = NULL;
@@ -103,18 +102,17 @@ API_clEnqueueNDRangeKernel gfn_clEnqueueNDRangeKernel;
入力
OpenCL_method : どのデバイスを選ぶか
unit_size : ブロックの単位サイズ
chunk_size: 分割された断片サイズ
src_max : ソース・ブロック個数
chunk_size = 0: 標準では分割しない
出力
return : エラー番号
src_max : 最大で何ブロックまでソースを読み込めるか
chunk_size : CPUスレッドの分割サイズ
OpenCL_method : 動作フラグいろいろ
*/
// 0=成功, 1エラー番号
int init_OpenCL(int unit_size, int *src_max, int *chunk_size)
int init_OpenCL(int unit_size, int chunk_size, int *src_max)
{
char buf[2048], *p_source;
int err = 0, i, j;
@@ -141,7 +139,7 @@ int init_OpenCL(int unit_size, int *src_max, int *chunk_size)
API_clGetKernelWorkGroupInfo fn_clGetKernelWorkGroupInfo;
cl_int ret;
cl_uint num_platforms = 0, num_devices = 0, num_groups, param_value;
cl_ulong param_value8, cache_size;
cl_ulong param_value8;
cl_platform_id platform_id[MAX_DEVICE], selected_platform; // Intel, AMD, Nvidia などドライバーの提供元
cl_device_id device_id[MAX_DEVICE], selected_device; // CPU や GPU など
cl_program program;
@@ -309,19 +307,14 @@ int init_OpenCL(int unit_size, int *src_max, int *chunk_size)
ret = fn_clGetDeviceInfo(device_id[j], CL_DEVICE_MAX_WORK_GROUP_SIZE, sizeof(size_t), &data_size, NULL);
if (ret != CL_SUCCESS)
continue;
ret = fn_clGetDeviceInfo(device_id[j], CL_DEVICE_HOST_UNIFIED_MEMORY, sizeof(cl_uint), &param_value, NULL);
if (ret != CL_SUCCESS)
continue;
if (param_value != 0)
param_value = 1;
// CL_DEVICE_HOST_UNIFIED_MEMORY は OpenCL 2.0 以降で非推奨になったので、参照しない
#ifdef DEBUG_OUTPUT
printf("MAX_COMPUTE_UNITS = %d\n", num_groups);
printf("MAX_WORK_GROUP_SIZE = %zd\n", data_size);
printf("HOST_UNIFIED_MEMORY = %d\n", param_value);
#endif
// MAX_COMPUTE_UNITS * MAX_WORK_GROUP_SIZE で計算力を測る、外付けGPUなら値を倍にする
count = (2 - param_value) * (int)data_size * num_groups;
// MAX_COMPUTE_UNITS * MAX_WORK_GROUP_SIZE で計算力を測る
count = (int)data_size * num_groups;
count *= OpenCL_method; // 符号を変える
//printf("prev = %d, now = %d\n", gpu_power, count);
if ((count > gpu_power) && (data_size >= 256) && // 256以上ないとテーブルを作れない
@@ -330,8 +323,6 @@ int init_OpenCL(int unit_size, int *src_max, int *chunk_size)
selected_device = device_id[j]; // 使うデバイスの ID
selected_platform = platform_id[i];
OpenCL_group_num = num_groups; // ワークグループ数は COMPUTE_UNITS 数にする
if (OpenCL_group_num > MAX_GROUP_NUM) // 制限を付けてローカルメモリーの消費を抑える
OpenCL_group_num = MAX_GROUP_NUM;
alloc_max = (size_t)param_value8;
// AMD Radeon ではメモリー領域が全体の 1/4 とは限らない
@@ -345,26 +336,6 @@ int init_OpenCL(int unit_size, int *src_max, int *chunk_size)
if ((cl_ulong)alloc_max > param_value8)
alloc_max = (size_t)param_value8;
}
cache_size = 0;
ret = fn_clGetDeviceInfo(device_id[j], CL_DEVICE_GLOBAL_MEM_CACHE_TYPE, sizeof(cl_uint), &num_groups, NULL);
if (ret == CL_SUCCESS){
#ifdef DEBUG_OUTPUT
printf("GLOBAL_MEM_CACHE_TYPE = %d\n", num_groups);
#endif
if (num_groups & 3){ // CL_READ_ONLY_CACHE or CL_READ_WRITE_CACHE
ret = fn_clGetDeviceInfo(device_id[j], CL_DEVICE_GLOBAL_MEM_CACHE_SIZE, sizeof(cl_ulong), &cache_size, NULL);
if (ret == CL_SUCCESS){
#ifdef DEBUG_OUTPUT
printf("GLOBAL_MEM_CACHE_SIZE = %I64d KB\n", cache_size >> 10);
#endif
if (param_value != 0){ // 内蔵 GPU なら CPU との共有キャッシュを活用する
if (cache_size >= 1048576) // サイズが小さい場合は分割しない
cache_size |= 0x40000000;
}
}
}
}
}
}
}
@@ -395,67 +366,28 @@ int init_OpenCL(int unit_size, int *src_max, int *chunk_size)
return (ret << 8) | 12;
// 計算方式を選択する
gpu_power = unit_size; // unit_size は MEM_UNIT の倍数になってる
if ((((cpu_flag & 0x101) == 1) || ((cpu_flag & 16) != 0)) && (sse_unit == 32)){
OpenCL_method = 2; // SSSE3 & ALTMAP または AVX2 ならデータの並び替え対応版を使う
if (cache_size & 0x40000000){ // 内蔵 GPU でキャッシュを利用できるなら、CPUスレッドと同じにする
j = cpu_cache & 0x7FFF8000; // CPUのキャッシュ上限サイズ
count = (int)(cache_size & 0x3FFFFFFF) / 4; // ただし、認識できるサイズの 1/4 までにする
if ((j == 0) || (j > count))
j = count;
count = 1;
while (gpu_power > j){ // 制限サイズより大きいなら
// 分割数を増やして chunk のサイズを試算してみる
count++;
gpu_power = (unit_size + count - 1) / count;
gpu_power = (gpu_power + (MEM_UNIT - 1)) & ~(MEM_UNIT - 1); // MEM_UNITの倍数にする
}
if (count > 1){
*chunk_size = gpu_power;
OpenCL_method = 3;
#ifdef DEBUG_OUTPUT
printf("gpu cache: limit size = %d, chunk size = %d, split = %d\n", j, gpu_power, count);
#endif
}
/*
// 32バイト単位のメモリーアクセスならキャッシュする必要なし計算速度が半減する・・・
} else if ((cache_size & 0x3FFFFFFF) > OpenCL_group_num * 4096){ // 2KB の倍はいるかも?
#ifdef DEBUG_OUTPUT
printf("gpu: cache size = %d, read size = %d\n", cache_size & 0x3FFFFFFF, OpenCL_group_num * 2048);
#endif
OpenCL_method = 1;
*/
}
} else if (((cpu_flag & 128) != 0) && (sse_unit == 256)){
OpenCL_method = 4; // JIT(SSE2) は bit ごとに上位から 16バイトずつ並ぶ
// ローカルのテーブルサイズが異なることに注意
// XOR 方式以外は 2KB (4バイト * 256項目 * 2個) 使う
// XOR (JIT) は 64バイト (4バイト * 16項目) 使う
#ifdef DEBUG_OUTPUT
// printf("4 KB cache (16-bytes * 256 work items), use if\n");
#endif
} else {
OpenCL_method = 1; // MMX用のコードは遅いので、キャッシュ最適化する必要が無い
OpenCL_method = 1; // 並び替えられてないデータ用
}
// work group 数が必要以上に多い場合は減らす
/*
if (OpenCL_method == 4){
// work item 一個が 16バイトずつ計算する、256個なら work group ごとに 4KB 担当する
data_size = unit_size / 4096;
} else
*/
if (OpenCL_method & 2){
if (OpenCL_method == 2){
// work item 一個が 8バイトずつ計算する、256個なら work group ごとに 2KB 担当する
data_size = unit_size / 2048;
data_size = chunk_size / 2048;
} else {
// work item 一個が 4バイトずつ計算する、256個なら work group ごとに 1KB 担当する
data_size = unit_size / 1024;
data_size = chunk_size / 1024;
}
if (OpenCL_group_num > data_size){
OpenCL_group_num = data_size;
printf("Number of work groups is reduced to %d\n", (int)OpenCL_group_num);
printf("Number of work groups is reduced to %zd\n", OpenCL_group_num);
}
// 最大で何ブロック分のメモリー領域を保持できるのか(ここではまだ確保しない)
@@ -469,9 +401,9 @@ int init_OpenCL(int unit_size, int *src_max, int *chunk_size)
printf("src buf : %zd KB (%d blocks), possible\n", data_size >> 10, count);
#endif
// 出力先は1ブロック分だけあればいい
// 出力先はchunk 1個分だけあればいい
// CL_MEM_ALLOC_HOST_PTRを使えばpinned memoryになるらしい
data_size = unit_size;
data_size = (chunk_size + 63) & ~63; // cache line sizes (64 bytes) の倍数にする
OpenCL_dst = gfn_clCreateBuffer(OpenCL_context, CL_MEM_WRITE_ONLY | CL_MEM_ALLOC_HOST_PTR, data_size, NULL, &ret);
if (ret != CL_SUCCESS)
return (ret << 8) | 13;
@@ -574,7 +506,7 @@ int init_OpenCL(int unit_size, int *src_max, int *chunk_size)
FreeResource(glob); // not required ?
// 定数を指定する
wsprintfA(buf, "-D BLK_SIZE=%d -D CHK_SIZE=%d", unit_size / 4, gpu_power / 4);
wsprintfA(buf, "-cl-fast-relaxed-math -D BLK_SIZE=%d", unit_size / 4);
// 使用する OpenCL デバイス用にコンパイルする
ret = fn_clBuildProgram(program, 1, &selected_device, buf, NULL, NULL);
@@ -768,11 +700,12 @@ int gpu_copy_blocks(
}
// ソース・ブロックを掛け算する
int gpu_multiply_blocks(
int gpu_multiply_chunks(
int src_num, // Number of multiplying source blocks
unsigned short *mat, // Matrix of numbers to multiply by
unsigned char *buf, // Products go here
int len) // Byte length
int offset, // Offset in each block
int length) // Byte length
{
unsigned __int64 *vram, *src, *dst;
size_t global_size, local_size;
@@ -787,6 +720,14 @@ int gpu_multiply_blocks(
ret = gfn_clSetKernelArg(OpenCL_kernel, 3, sizeof(int), &src_num);
if (ret != CL_SUCCESS)
return (ret << 8) | 103;
offset /= 4; // 4バイト整数単位にする
ret = gfn_clSetKernelArg(OpenCL_kernel, 4, sizeof(int), &offset);
if (ret != CL_SUCCESS)
return (ret << 8) | 104;
length /= 4; // 4バイト整数単位にする
ret = gfn_clSetKernelArg(OpenCL_kernel, 5, sizeof(int), &length);
if (ret != CL_SUCCESS)
return (ret << 8) | 105;
// カーネル並列実行
local_size = 256; // テーブルやキャッシュのため、work item 数は 256に固定する
@@ -797,18 +738,18 @@ int gpu_multiply_blocks(
return (ret << 8) | 11;
// 出力内容をホスト側に反映させる
vram = gfn_clEnqueueMapBuffer(OpenCL_command, OpenCL_dst, CL_TRUE, CL_MAP_READ, 0, len, 0, NULL, NULL, &ret);
vram = gfn_clEnqueueMapBuffer(OpenCL_command, OpenCL_dst, CL_TRUE, CL_MAP_READ, 0, length * 4, 0, NULL, NULL, &ret);
if (ret != CL_SUCCESS)
return (ret << 8) | 12;
// 8バイトごとに XOR する (SSE2 で XOR しても速くならず)
src = vram;
dst = (unsigned __int64 *)buf;
while (len > 0){
while (length > 0){
*dst ^= *src;
dst++;
src++;
len -= 8;
length -= 2;
}
// ホスト側でデータを変更しなくても、clEnqueueMapBufferと対で呼び出さないといけない

View File

@@ -10,20 +10,21 @@ extern "C" {
extern int OpenCL_method;
int init_OpenCL(int unit_size, int *src_max, int *chunk_size);
int init_OpenCL(int unit_size, int chunk_size, int *src_max);
int free_OpenCL(void);
void info_OpenCL(char *buf, int buf_size);
int gpu_copy_blocks(
unsigned char *data,
int unit_size,
int src_end);
int src_num);
int gpu_multiply_blocks(
int gpu_multiply_chunks(
int src_num, // Number of multiplying source blocks
unsigned short *mat, // Matrix of numbers to multiply by
unsigned char *buf, // Products go here
int len); // Byte length
int offset, // Offset in each block
int length); // Byte length
int gpu_finish(void);

View File

@@ -1,5 +1,5 @@
// md5_crc.c
// Copyright : 2022-10-01 Yutaka Sawada
// Copyright : 2023-08-28 Yutaka Sawada
// License : GPL
#ifndef _UNICODE
@@ -672,7 +672,7 @@ time1_start = GetTickCount();
// バッファー・サイズが大きいのでヒープ領域を使う
for (io_size = IO_SIZE; io_size < 1048576; io_size += IO_SIZE){ // 1 MB までにする
if ((io_size + IO_SIZE > (cpu_cache << 17)) || ((__int64)(io_size + IO_SIZE) * 4 > file_left))
if ((io_size + IO_SIZE > (cpu_cache & 0xFFFE0000)) || ((__int64)(io_size + IO_SIZE) * 4 > file_left))
break;
}
buf1 = _aligned_malloc(io_size * 2, 64);
@@ -867,7 +867,7 @@ DWORD WINAPI file_hash_crc2(LPVOID lpParameter)
// バッファー・サイズが大きいのでヒープ領域を使う
prog_tick = 1;
for (io_size = IO_SIZE; io_size < 1048576; io_size += IO_SIZE){ // IO_SIZE の倍数で 1 MB までにする
if ((io_size + IO_SIZE > (cpu_cache << 17)) || ((__int64)(io_size + IO_SIZE) * 4 > file_left))
if ((io_size + IO_SIZE > (cpu_cache & 0xFFFE0000)) || ((__int64)(io_size + IO_SIZE) * 4 > file_left))
break;
prog_tick++;
}
@@ -1304,7 +1304,7 @@ DWORD WINAPI file_hash_background(LPVOID lpParameter)
// バッファー・サイズが大きいのでヒープ領域を使う
for (io_size = IO_SIZE; io_size < 1048576; io_size += IO_SIZE){ // IO_SIZE の倍数で 1 MB までにする
if ((io_size + IO_SIZE > (cpu_cache << 17)) || ((__int64)(io_size + IO_SIZE) * 4 > file_size))
if ((io_size + IO_SIZE > (cpu_cache & 0xFFFE0000)) || ((__int64)(io_size + IO_SIZE) * 4 > file_size))
break;
}
//printf("\n io_size = %d\n", io_size);

View File

@@ -1,5 +1,5 @@
// par2.c
// Copyright : 2023-03-15 Yutaka Sawada
// Copyright : 2023-09-21 Yutaka Sawada
// License : GPL
#ifndef _UNICODE
@@ -112,12 +112,12 @@ int par2_create(
err = -12;
} else {
// メモリーを確保できるか試す
err = read_block_num(parity_num, cpu_num - 1, 0, 256);
err = read_block_num(parity_num, 0, 256);
if (err == 0)
err = -13;
}
#ifdef TIMER
printf("read_block_num = %d\n", read_block_num(parity_num, cpu_num - 1, 0, 256));
printf("read_block_num = %d\n", read_block_num(parity_num, 0, 256));
#endif
if (err > 0){ // 1-pass方式が可能
#ifdef TIMER

View File

@@ -1,5 +1,5 @@
// par2_cmd.c
// Copyright : 2023-03-18 Yutaka Sawada
// Copyright : 2023-09-18 Yutaka Sawada
// License : GPL
#ifndef _UNICODE
@@ -86,7 +86,7 @@ static void print_environment(void)
printf("CPU thread\t: %d / %d\n", cpu_num & 0xFFFF, cpu_num >> 24);
cpu_num &= 0xFFFF; // 利用するコア数だけにしておく
printf("CPU cache limit : %d KB, %d KB\n", (cpu_cache & 0x7FFF8000) >> 10, (cpu_cache & 0x00007FFF) << 7);
printf("CPU cache limit : %d KB, %d KB\n", (cpu_flag & 0xFFFF0000) >> 10, (cpu_cache & 0xFFFE0000) >> 10);
#ifndef _WIN64 // 32-bit 版は MMX, SSE2, SSSE3 のどれかを表示する
printf("CPU extra\t:");
if (cpu_flag & 1){
@@ -1481,39 +1481,39 @@ ri= switch_set & 0x00040000
k = (k * 10) + (tmp_p[j] - '0');
j++;
}
if (k & 32){ // GPU を使う
if (k & 256){ // GPU を使う
OpenCL_method = 1; // Faster GPU
} else if (k & 64){
} else if (k & 512){
OpenCL_method = -1; // Slower GPU
}
if (k & 16) // SSSE3 を使わない
cpu_flag &= 0xFFFFFFFE;
if (k & 128) // CLMUL を使わない、SSSE3 の古いエンコーダーを使う
if (k & 1024) // CLMUL を使わない、SSSE3 の古いエンコーダーを使う
cpu_flag = (cpu_flag & 0xFFFFFFF7) | 0x100;
if (k & 256) // JIT(SSE2) を使わない
if (k & 2048) // JIT(SSE2) を使わない
cpu_flag &= 0xFFFFFF7F;
if (k & 512) // AVX2 を使わない
if (k & 4096) // SSSE3 を使わない
cpu_flag &= 0xFFFFFFFE;
if (k & 8192) // AVX2 を使わない
cpu_flag &= 0xFFFFFFEF;
if (k & 15){ // 使用するコア数を変更する
k &= 15; // 115 の範囲
if (k & 255){ // 使用するコア数を変更する
k &= 255; // 1255 の範囲
// printf("\n lc# = %d , logical = %d, physical = %d \n", k, cpu_num >> 24, (cpu_num & 0x00FF0000) >> 16);
if (k == 12){ // 物理コア数の 1/4 にする
if (k == 251){ // 物理コア数の 1/4 にする
k = ((cpu_num & 0x00FF0000) >> 16) / 4;
} else if (k == 13){ // 物理コア数の半分にする
} else if (k == 252){ // 物理コア数の半分にする
k = ((cpu_num & 0x00FF0000) >> 16) / 2;
} else if (k == 14){ // 物理コア数の 3/4 にする
} else if (k == 253){ // 物理コア数の 3/4 にする
k = (((cpu_num & 0x00FF0000) >> 16) * 3) / 4;
} else if (k == 15){ // 物理コア数にする
k = (cpu_num & 0x00FF0000) >> 16;
if (k >= 6)
k--; // 物理コア数が 6以上なら、1個減らす
} else if (k > (cpu_num >> 24)){
k = cpu_num >> 24; // 論理コア数を超えないようにする
} else if (k == 254){ // 物理コア数より減らす
k = ((cpu_num & 0x00FF0000) >> 16) - 1;
} else if (k == 255){ // 物理コア数より増やす
k = ((cpu_num & 0x00FF0000) >> 16) + 1;
}
if (k > MAX_CPU){
k = MAX_CPU;
} else if (k < 1){
k = 1;
} else if (k > (cpu_num >> 24)){
k = cpu_num >> 24; // 論理コア数を超えないようにする
}
cpu_num = (cpu_num & 0xFFFF0000) | k; // 指定されたコア数を下位に配置する
}

View File

@@ -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 方式でブロックを断片化させる

View File

@@ -10,7 +10,6 @@ extern "C" {
// Read all source & Keep some parity 方式
// 部分的なエンコードを行う最低ブロック数
#define PART_MAX_RATE 1 // ソース・ブロック数の 1/2 = 50%
#define PART_MIN_RATE 5 // ソース・ブロック数の 1/32 = 3.1%
// Read some source & Keep all parity 方式
@@ -33,7 +32,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)

View File

@@ -1,8 +1,8 @@
1 RT_STRING ".\\source.cl"
1 VERSIONINFO
FILEVERSION 1,3,2,8
PRODUCTVERSION 1,3,2,0
FILEVERSION 1,3,3,0
PRODUCTVERSION 1,3,3,0
FILEOS 0x40004
FILETYPE 0x1
{
@@ -13,8 +13,8 @@ BLOCK "StringFileInfo"
VALUE "FileDescription", "PAR2 client"
VALUE "LegalCopyright", "Copyright (C) 2023 Yutaka Sawada"
VALUE "ProductName", "par2j"
VALUE "FileVersion", "1.3.2.8"
VALUE "ProductVersion", "1.3.2.0"
VALUE "FileVersion", "1.3.3.0"
VALUE "ProductVersion", "1.3.3.0"
}
}

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -18,7 +18,9 @@ __kernel void method1(
__global uint *src,
__global uint *dst,
__global ushort *factors,
int blk_num)
int blk_num,
int offset,
int length)
{
__local uint mtab[512];
int i, blk;
@@ -27,14 +29,15 @@ __kernel void method1(
const int work_size = get_global_size(0);
const int table_id = get_local_id(0);
for (i = work_id; i < BLK_SIZE; i += work_size)
src += offset;
for (i = work_id; i < length; i += work_size)
dst[i] = 0;
for (blk = 0; blk < blk_num; blk++){
calc_table(mtab, table_id, factors[blk]);
barrier(CLK_LOCAL_MEM_FENCE);
for (i = work_id; i < BLK_SIZE; i += work_size){
for (i = work_id; i < length; i += work_size){
v = src[i];
sum = mtab[(uchar)(v >> 16)] ^ mtab[256 + (v >> 24)];
sum <<= 16;
@@ -50,7 +53,9 @@ __kernel void method2(
__global uint *src,
__global uint *dst,
__global ushort *factors,
int blk_num)
int blk_num,
int offset,
int length)
{
__local uint mtab[512];
int i, blk, pos;
@@ -59,7 +64,8 @@ __kernel void method2(
const int work_size = get_global_size(0) * 2;
const int table_id = get_local_id(0);
for (i = work_id; i < BLK_SIZE; i += work_size){
src += offset;
for (i = work_id; i < length; i += work_size){
dst[i ] = 0;
dst[i + 1] = 0;
}
@@ -68,7 +74,7 @@ __kernel void method2(
calc_table(mtab, table_id, factors[blk]);
barrier(CLK_LOCAL_MEM_FENCE);
for (i = work_id; i < BLK_SIZE; i += work_size){
for (i = work_id; i < length; i += work_size){
pos = (i & ~7) + ((i & 7) >> 1);
lo = src[pos ];
hi = src[pos + 4];
@@ -86,64 +92,13 @@ __kernel void method2(
}
}
__kernel void method3(
__global uint *src,
__global uint *dst,
__global ushort *factors,
int blk_num)
{
__global uint *blk_src;
__local uint mtab[512];
int i, blk, chk_size, remain, pos;
uint lo, hi, sum1, sum2;
const int work_id = get_global_id(0) * 2;
const int work_size = get_global_size(0) * 2;
const int table_id = get_local_id(0);
remain = BLK_SIZE;
chk_size = CHK_SIZE;
while (remain > 0){
if (chk_size > remain)
chk_size = remain;
for (i = work_id; i < chk_size; i += work_size){
dst[i ] = 0;
dst[i + 1] = 0;
}
blk_src = src;
for (blk = 0; blk < blk_num; blk++){
calc_table(mtab, table_id, factors[blk]);
barrier(CLK_LOCAL_MEM_FENCE);
for (i = work_id; i < chk_size; i += work_size){
pos = (i & ~7) + ((i & 7) >> 1);
lo = blk_src[pos ];
hi = blk_src[pos + 4];
sum1 = mtab[(uchar)(lo >> 16)] ^ mtab[256 + (uchar)(hi >> 16)];
sum2 = mtab[lo >> 24] ^ mtab[256 + (hi >> 24)];
sum1 <<= 16;
sum2 <<= 16;
sum1 ^= mtab[(uchar)lo] ^ mtab[256 + (uchar)hi];
sum2 ^= mtab[(uchar)(lo >> 8)] ^ mtab[256 + (uchar)(hi >> 8)];
dst[pos ] ^= (sum1 & 0x00FF00FF) | ((sum2 & 0x00FF00FF) << 8);
dst[pos + 4] ^= ((sum1 & 0xFF00FF00) >> 8) | (sum2 & 0xFF00FF00);
}
blk_src += BLK_SIZE;
barrier(CLK_LOCAL_MEM_FENCE);
}
src += CHK_SIZE;
dst += CHK_SIZE;
remain -= CHK_SIZE;
}
}
__kernel void method4(
__global uint *src,
__global uint *dst,
__global ushort *factors,
int blk_num)
int blk_num,
int offset,
int length)
{
__local int table[16];
__local uint cache[256];
@@ -152,7 +107,8 @@ __kernel void method4(
const int work_id = get_global_id(0);
const int work_size = get_global_size(0);
for (i = work_id; i < BLK_SIZE; i += work_size)
src += offset;
for (i = work_id; i < length; i += work_size)
dst[i] = 0;
for (blk = 0; blk < blk_num; blk++){
@@ -166,7 +122,7 @@ __kernel void method4(
}
barrier(CLK_LOCAL_MEM_FENCE);
for (i = work_id; i < BLK_SIZE; i += work_size){
for (i = work_id; i < length; i += work_size){
pos = i & 255;
cache[pos] = src[i];
barrier(CLK_LOCAL_MEM_FENCE);

View File

@@ -1,2 +1,2 @@
#define FILE_VERSION "1.3.2.8" // ファイルのバージョン番号
#define PRODUCT_VERSION "1.3.2" // 製品のバージョン番号
#define FILE_VERSION "1.3.3.0" // ファイルのバージョン番号
#define PRODUCT_VERSION "1.3.3" // 製品のバージョン番号