From d75ab788c52471d15911efa524c635bd53a127e4 Mon Sep 17 00:00:00 2001 From: Hans Pabst Date: Mon, 7 Oct 2024 10:59:45 +0200 Subject: [PATCH] ocl: follow-on for #846 This is further speeding up host memory allocation. So far, an OpenCL cl_mem object was created with no host-pointer and it was up to the OpenCL runtime to allocate the host memory. It turns out, this is horribly slow for GH200 stack (for unknown reasons). It was only helpful to mark such OpenCL allocated host memory as "not worth to transfer initially" (CL_MAP_WRITE_INVALIDATE_REGION); a bug in itself. Normally, allocating host memory by relying on the OpenCL runtime yields best performance at least when this memory is the origin/destination of a (PCIe-)transfer. However, GH200 SW stack seems to struggle with this idea. Introduced code path (covered by XHINTS) specific to Nvidia, which simply wraps a malloc'ed pointer (host memory). * Implemented malloc based c_dbcsr_acc_host_mem_allocate. * Introduced compile-time ACC_OPENCL_XHINTS. * Updated tuned parameters (Mi250 and GH200). * Removed unused function (m_getuid). --- src/acc/opencl/acc_opencl.c | 16 ++++-- src/acc/opencl/acc_opencl.h | 3 ++ src/acc/opencl/acc_opencl_mem.c | 54 +++++++++++++------ src/acc/opencl/acc_opencl_stream.c | 2 + .../opencl/smm/params/tune_multiply_GH200.csv | 8 +++ .../opencl/smm/params/tune_multiply_Mi250.csv | 2 + src/base/dbcsr_machine_posix.f90 | 16 +----- 7 files changed, 66 insertions(+), 35 deletions(-) diff --git a/src/acc/opencl/acc_opencl.c b/src/acc/opencl/acc_opencl.c index f751a384488..574b685c4f1 100644 --- a/src/acc/opencl/acc_opencl.c +++ b/src/acc/opencl/acc_opencl.c @@ -217,8 +217,8 @@ int c_dbcsr_acc_init(void) { cl_platform_id platforms[ACC_OPENCL_MAXNDEVS] = {NULL}; cl_device_id devices[ACC_OPENCL_MAXNDEVS]; char buffer[ACC_OPENCL_BUFFERSIZE]; + const char *const env_devsplit = getenv("ACC_OPENCL_DEVSPLIT"), *const env_priority = getenv("ACC_OPENCL_PRIORITY"); const char *const env_devmatch = getenv("ACC_OPENCL_DEVMATCH"), *const env_devtype = getenv("ACC_OPENCL_DEVTYPE"); - const char *const env_priority = getenv("ACC_OPENCL_PRIORITY"), *const env_xhints = getenv("ACC_OPENCL_XHINTS"); const char *const env_verbose = getenv("ACC_OPENCL_VERBOSE"), *const env_debug = getenv("ACC_OPENCL_DEBUG"); const char *const env_device = getenv("ACC_OPENCL_DEVICE"), *const env_dump_acc = getenv("ACC_OPENCL_DUMP"); const char *const env_timer = getenv("ACC_OPENCL_TIMER"), *const env_nlocks = getenv("ACC_OPENCL_NLOCKS"); @@ -229,14 +229,20 @@ int c_dbcsr_acc_init(void) { # endif const char *const env_neo = getenv("NEOReadDebugKeys"), *const env_wa = getenv("ACC_OPENCL_WA"); const int neo = (NULL == env_neo ? 1 : atoi(env_neo)); +# if defined(ACC_OPENCL_XHINTS) + const char* const env_xhints = (ACC_OPENCL_XHINTS); + const int xhints_default = 1 + 2 + 4 + 8; +# else + const char* const env_xhints = NULL; + const int xhints_default = 0; +# endif # if defined(ACC_OPENCL_ASYNC) const char* const env_async = (ACC_OPENCL_ASYNC); - const int async_default = 3; + const int async_default = 1 + 2; # else const char* const env_async = NULL; const int async_default = 0; # endif - const char* const env_devsplit = getenv("ACC_OPENCL_DEVSPLIT"); /*const char* const env_nranks = getenv("MPI_LOCALNRANKS"); const cl_uint nranks = LIBXSMM_MAX(NULL != env_nranks ? atoi(env_nranks) : 1, 1);*/ const cl_int devsplit = (NULL == env_devsplit ? /*(1 < nranks ? -1 : 0)*/ 0 : atoi(env_devsplit)); @@ -274,7 +280,7 @@ int c_dbcsr_acc_init(void) { : c_dbcsr_acc_opencl_config.lock_main); c_dbcsr_acc_opencl_config.verbosity = (NULL == env_verbose ? 0 : atoi(env_verbose)); c_dbcsr_acc_opencl_config.priority = (NULL == env_priority ? /*default*/ 3 : atoi(env_priority)); - c_dbcsr_acc_opencl_config.xhints = (NULL == env_xhints ? (1 + 2 + 4) : atoi(env_xhints)); + c_dbcsr_acc_opencl_config.xhints = (NULL == env_xhints ? xhints_default : atoi(env_xhints)); c_dbcsr_acc_opencl_config.async = (NULL == env_async ? async_default : atoi(env_async)); c_dbcsr_acc_opencl_config.dump = (NULL == env_dump ? /*default*/ 0 : atoi(env_dump)); c_dbcsr_acc_opencl_config.debug = (NULL == env_debug ? c_dbcsr_acc_opencl_config.dump : atoi(env_debug)); @@ -1109,7 +1115,7 @@ int c_dbcsr_acc_opencl_set_active_device(ACC_OPENCL_LOCKTYPE* lock, int device_i else { c_dbcsr_acc_opencl_config.device.wgsize[2] = 0; } -# if defined(ACC_OPENCL_MEM_DEVPTR) +# if defined(ACC_OPENCL_XHINTS) && defined(ACC_OPENCL_MEM_DEVPTR) if (0 != (1 & c_dbcsr_acc_opencl_config.xhints) && 2 <= *c_dbcsr_acc_opencl_config.device.std_level && 0 != c_dbcsr_acc_opencl_config.device.intel && 0 == c_dbcsr_acc_opencl_config.device.unified && EXIT_SUCCESS == clGetDeviceInfo(active_id, CL_DEVICE_PLATFORM, sizeof(cl_platform_id), &platform, NULL) && diff --git a/src/acc/opencl/acc_opencl.h b/src/acc/opencl/acc_opencl.h index cd6639983fd..fadcf20f976 100644 --- a/src/acc/opencl/acc_opencl.h +++ b/src/acc/opencl/acc_opencl.h @@ -104,6 +104,9 @@ #if !defined(ACC_OPENCL_ASYNC) && 1 # define ACC_OPENCL_ASYNC getenv("ACC_OPENCL_ASYNC") #endif +#if !defined(ACC_OPENCL_XHINTS) && 1 +# define ACC_OPENCL_XHINTS getenv("ACC_OPENCL_XHINTS") +#endif #if !defined(ACC_OPENCL_STREAM_PRIORITIES) && 0 # if defined(CL_QUEUE_PRIORITY_KHR) # define ACC_OPENCL_STREAM_PRIORITIES diff --git a/src/acc/opencl/acc_opencl_mem.c b/src/acc/opencl/acc_opencl_mem.c index e725f239686..b66ac677acb 100644 --- a/src/acc/opencl/acc_opencl_mem.c +++ b/src/acc/opencl/acc_opencl_mem.c @@ -168,6 +168,8 @@ int c_dbcsr_acc_opencl_info_devptr( int c_dbcsr_acc_host_mem_allocate(void** host_mem, size_t nbytes, void* stream) { const size_t size_meminfo = sizeof(c_dbcsr_acc_opencl_info_memptr_t); int result = EXIT_SUCCESS, alignment = sizeof(void*); + cl_mem_flags flags = CL_MEM_ALLOC_HOST_PTR; + void* host_ptr = NULL; cl_mem memory = NULL; # if defined(__DBCSR_ACC) && defined(ACC_OPENCL_PROFILE) int routine_handle; @@ -186,16 +188,25 @@ int c_dbcsr_acc_host_mem_allocate(void** host_mem, size_t nbytes, void* stream) EXIT_SUCCESS == c_dbcsr_acc_opencl_set_active_device(NULL /*lock*/, (int)c_dbcsr_acc_opencl_config.device.uid)); } # endif - memory = clCreateBuffer(c_dbcsr_acc_opencl_config.device.context, CL_MEM_ALLOC_HOST_PTR, nbytes, NULL /*host_ptr*/, &result); +# if defined(ACC_OPENCL_XHINTS) + if (0 != (8 & c_dbcsr_acc_opencl_config.xhints) && (0 != c_dbcsr_acc_opencl_config.device.nv || NULL != (ACC_OPENCL_XHINTS))) { + host_ptr = malloc(nbytes); + if (NULL != host_ptr) flags = CL_MEM_USE_HOST_PTR; + } +# endif + memory = clCreateBuffer(c_dbcsr_acc_opencl_config.device.context, flags, nbytes, host_ptr, &result); if (EXIT_SUCCESS == result) { - const c_dbcsr_acc_opencl_stream_t* const str = (NULL != stream ? ACC_OPENCL_STREAM(stream) - : c_dbcsr_acc_opencl_stream_default()); - void* const mapped = clEnqueueMapBuffer(str->queue, memory, CL_TRUE /*always block*/, -# if defined(CL_VERSION_1_2) || defined(CL_MAP_WRITE_INVALIDATE_REGION) - (4 & c_dbcsr_acc_opencl_config.xhints) ? CL_MAP_WRITE_INVALIDATE_REGION : -# endif - (CL_MAP_READ | CL_MAP_WRITE), - 0 /*offset*/, nbytes, 0, NULL, NULL, &result); + void* mapped = host_ptr; + if (NULL == host_ptr) { + const c_dbcsr_acc_opencl_stream_t* const str = (NULL != stream ? ACC_OPENCL_STREAM(stream) + : c_dbcsr_acc_opencl_stream_default()); + mapped = clEnqueueMapBuffer(str->queue, memory, CL_TRUE /*always block*/, +# if defined(ACC_OPENCL_XHINTS) && (defined(CL_VERSION_1_2) || defined(CL_MAP_WRITE_INVALIDATE_REGION)) + (4 & c_dbcsr_acc_opencl_config.xhints) ? CL_MAP_WRITE_INVALIDATE_REGION : +# endif + (CL_MAP_READ | CL_MAP_WRITE), + 0 /*offset*/, nbytes, 0, NULL, NULL, &result); + } assert(EXIT_SUCCESS == result || NULL == mapped); if (EXIT_SUCCESS == result) { const uintptr_t address = (uintptr_t)mapped; @@ -214,6 +225,7 @@ int c_dbcsr_acc_host_mem_allocate(void** host_mem, size_t nbytes, void* stream) if (EXIT_SUCCESS != result) { if (NULL != memory) ACC_OPENCL_EXPECT(EXIT_SUCCESS == clReleaseMemObject(memory)); *host_mem = NULL; + free(host_ptr); } assert(EXIT_SUCCESS == result || NULL == *host_mem); # if defined(__DBCSR_ACC) && defined(ACC_OPENCL_PROFILE) @@ -235,13 +247,25 @@ int c_dbcsr_acc_host_mem_deallocate(void* host_mem, void* stream) { c_dbcsr_acc_opencl_info_memptr_t* const meminfo = c_dbcsr_acc_opencl_info_hostptr(host_mem); if (NULL != meminfo->memory) { const c_dbcsr_acc_opencl_info_memptr_t info = *meminfo; /* copy meminfo prior to unmap */ - const c_dbcsr_acc_opencl_stream_t* const str = (NULL != stream ? ACC_OPENCL_STREAM(stream) - : c_dbcsr_acc_opencl_stream_default()); + void* host_ptr = NULL; int result_release; - cl_event event; - assert(NULL != str && NULL != str->queue); - result = clEnqueueUnmapMemObject(str->queue, info.memory, info.memptr, 0, NULL, &event); - if (NULL == stream && EXIT_SUCCESS == result) result = clWaitForEvents(1, &event); +# if defined(ACC_OPENCL_XHINTS) + if (0 != (8 & c_dbcsr_acc_opencl_config.xhints) && + (0 != c_dbcsr_acc_opencl_config.device.nv || NULL != (ACC_OPENCL_XHINTS)) && + EXIT_SUCCESS == clGetMemObjectInfo(info.memory, CL_MEM_HOST_PTR, sizeof(void*), &host_ptr, NULL) && NULL != host_ptr) + { + free(host_ptr); + } + if (NULL == host_ptr) +# endif + { + const c_dbcsr_acc_opencl_stream_t* const str = (NULL != stream ? ACC_OPENCL_STREAM(stream) + : c_dbcsr_acc_opencl_stream_default()); + cl_event event; + assert(NULL != str && NULL != str->queue); + result = clEnqueueUnmapMemObject(str->queue, info.memory, info.memptr, 0, NULL, &event); + if (NULL == stream && EXIT_SUCCESS == result) result = clWaitForEvents(1, &event); + } result_release = clReleaseMemObject(info.memory); if (EXIT_SUCCESS == result) result = result_release; } diff --git a/src/acc/opencl/acc_opencl_stream.c b/src/acc/opencl/acc_opencl_stream.c index 41297015ba8..29ade32dba8 100644 --- a/src/acc/opencl/acc_opencl_stream.c +++ b/src/acc/opencl/acc_opencl_stream.c @@ -117,6 +117,7 @@ int c_dbcsr_acc_stream_create(void** stream_p, const char* name, int priority) { if (NULL != c_dbcsr_acc_opencl_config.device.context) # endif { +# if defined(ACC_OPENCL_XHINTS) if ((2 & c_dbcsr_acc_opencl_config.xhints) && 0 != c_dbcsr_acc_opencl_config.device.intel) { /* enable queue families */ struct { cl_command_queue_properties properties; @@ -141,6 +142,7 @@ int c_dbcsr_acc_stream_create(void** stream_p, const char* name, int priority) { } } } +# endif if ((c_dbcsr_acc_opencl_timer_device == c_dbcsr_acc_opencl_config.timer) && (3 <= c_dbcsr_acc_opencl_config.verbosity || 0 > c_dbcsr_acc_opencl_config.verbosity)) { diff --git a/src/acc/opencl/smm/params/tune_multiply_GH200.csv b/src/acc/opencl/smm/params/tune_multiply_GH200.csv index 000f4cfbf04..3401f468898 100644 --- a/src/acc/opencl/smm/params/tune_multiply_GH200.csv +++ b/src/acc/opencl/smm/params/tune_multiply_GH200.csv @@ -170,6 +170,9 @@ NVIDIA GH200 480GB [0x3528];3;9;22;9;30000;0;16;9;1;7;1;0;-1;0;0;0;1;0;1;0;0;0 NVIDIA GH200 480GB [0x3528];3;9;22;16;30000;0;12;9;1;5;1;-2;-2;0;0;0;1;0;1;2;0;0 NVIDIA GH200 480GB [0x3528];3;9;22;22;30000;0;15;9;1;8;1;1;0;0;0;0;1;0;1;2;0;0 NVIDIA GH200 480GB [0x3528];3;9;22;32;30000;0;15;9;1;7;1;-1;0;0;0;0;1;0;1;2;0;0 +NVIDIA GH200 480GB [0x3528];3;9;32;9;30000;0;16;9;1;6;32;1;0;0;0;0;1;0;1;2;0;0 +NVIDIA GH200 480GB [0x3528];3;9;32;22;30000;0;10;9;1;6;32;1;-1;0;0;0;1;0;1;2;0;0 +NVIDIA GH200 480GB [0x3528];3;9;32;32;30000;0;12;9;1;2;32;0;1;0;0;0;1;0;1;0;0;0 NVIDIA GH200 480GB [0x3528];3;10;4;4;30000;0;8;8;1;5;1;0;1;0;0;0;1;0;0;0;0;0 NVIDIA GH200 480GB [0x3528];3;10;4;10;30000;0;10;8;1;3;10;0;3;0;0;0;1;0;0;2;0;0 NVIDIA GH200 480GB [0x3528];3;10;10;4;30000;0;17;10;1;7;1;0;-1;0;0;0;1;0;1;0;0;0 @@ -256,6 +259,9 @@ NVIDIA GH200 480GB [0x3528];3;22;22;9;30000;0;23;22;1;17;1;0;0;0;0;0;1;0;1;0;0;0 NVIDIA GH200 480GB [0x3528];3;22;22;16;30000;0;20;22;1;5;1;1;-1;0;0;0;1;0;1;2;0;0 NVIDIA GH200 480GB [0x3528];3;22;22;22;30000;0;23;22;1;14;1;-1;-2;0;0;0;1;0;1;2;0;0 NVIDIA GH200 480GB [0x3528];3;22;22;32;30000;0;30;22;1;7;1;0;-1;0;0;0;1;0;1;2;0;0 +NVIDIA GH200 480GB [0x3528];3;22;32;9;30000;0;17;22;1;20;32;-1;0;0;0;0;1;0;1;2;0;0 +NVIDIA GH200 480GB [0x3528];3;22;32;22;30000;0;20;22;1;9;32;0;-1;0;0;0;1;0;1;2;0;0 +NVIDIA GH200 480GB [0x3528];3;22;32;32;30000;0;40;22;1;17;32;-1;3;0;0;0;1;0;1;0;0;0 NVIDIA GH200 480GB [0x3528];3;23;23;23;30000;0;20;23;1;16;23;1;0;0;0;0;1;0;1;2;0;0 NVIDIA GH200 480GB [0x3528];3;24;24;24;30000;0;20;24;1;16;1;-2;-2;0;0;0;1;0;1;0;0;0 NVIDIA GH200 480GB [0x3528];3;25;4;4;30000;0;8;8;1;13;25;0;-2;0;0;0;1;0;2;2;0;0 @@ -288,8 +294,10 @@ NVIDIA GH200 480GB [0x3528];3;32;17;17;30000;0;20;32;1;4;1;-2;-1;0;0;0;1;0;1;2;0 NVIDIA GH200 480GB [0x3528];3;32;17;32;30000;0;10;8;1;23;1;1;4;0;0;0;1;0;1;2;0;0 NVIDIA GH200 480GB [0x3528];3;32;32;4;30000;0;26;32;1;4;1;0;-1;0;0;0;1;0;1;2;0;0 NVIDIA GH200 480GB [0x3528];3;32;32;5;30000;0;29;32;1;11;1;1;0;0;0;0;1;0;1;2;0;0 +NVIDIA GH200 480GB [0x3528];3;32;32;9;30000;0;23;32;1;21;32;0;-2;0;0;0;1;0;1;2;0;0 NVIDIA GH200 480GB [0x3528];3;32;32;13;30000;0;30;32;1;6;1;0;-1;0;0;0;1;0;1;2;0;0 NVIDIA GH200 480GB [0x3528];3;32;32;17;30000;0;23;32;1;22;1;0;-1;0;0;0;1;0;1;2;0;0 +NVIDIA GH200 480GB [0x3528];3;32;32;22;30000;0;20;32;1;27;32;0;-2;0;0;0;1;0;1;2;0;0 NVIDIA GH200 480GB [0x3528];3;32;32;32;30000;0;19;32;1;29;1;-1;-2;0;0;0;1;0;1;2;0;0 NVIDIA GH200 480GB [0x3528];3;35;35;35;30000;0;57;24;1;2;1;0;2;0;0;0;1;0;1;0;0;0 NVIDIA GH200 480GB [0x3528];3;36;36;36;30000;0;15;36;1;2;1;-1;3;0;0;0;1;0;1;0;0;0 diff --git a/src/acc/opencl/smm/params/tune_multiply_Mi250.csv b/src/acc/opencl/smm/params/tune_multiply_Mi250.csv index 4410500501c..d73036e2ca9 100644 --- a/src/acc/opencl/smm/params/tune_multiply_Mi250.csv +++ b/src/acc/opencl/smm/params/tune_multiply_Mi250.csv @@ -281,6 +281,7 @@ gfx90a [0x989f];3;12;23;12;30000;0;20;12;1;1;1;0;-2;0;0;1;1;1;2;2;1;0 gfx90a [0x989f];3;12;23;23;30000;0;3;8;1;8;1;-2;-2;0;0;0;1;0;1;2;0;0 gfx90a [0x989f];3;13;13;13;30000;0;12;13;1;11;1;0;-1;0;1;0;1;1;0;2;1;0 gfx90a [0x989f];3;13;13;23;30000;0;12;13;1;1;1;-2;-2;0;1;0;1;1;2;0;1;0 +gfx90a [0x989f];3;13;13;32;30000;0;3;13;1;9;13;0;2;0;0;0;1;0;1;2;0;0 gfx90a [0x989f];3;13;23;13;30000;0;30;8;1;10;1;1;2;0;0;0;1;0;1;0;0;0 gfx90a [0x989f];3;13;23;23;30000;0;3;8;1;6;1;-1;2;0;0;0;1;0;1;2;0;0 gfx90a [0x989f];3;14;14;14;30000;0;10;14;1;10;1;0;0;0;1;1;1;1;0;2;0;0 @@ -351,6 +352,7 @@ gfx90a [0x989f];3;18;23;23;30000;0;4;8;1;16;1;0;3;0;0;0;1;0;1;2;0;0 gfx90a [0x989f];3;19;19;19;30000;0;40;8;1;10;1;1;3;0;0;0;1;0;1;2;0;0 gfx90a [0x989f];3;19;19;23;30000;0;40;8;1;15;1;-1;-2;0;0;0;1;0;1;0;0;0 gfx90a [0x989f];3;23;23;23;30000;0;4;8;1;22;23;-1;3;0;0;0;1;0;1;0;0;0 +gfx90a [0x989f];3;28;28;28;30000;0;3;28;1;28;28;-2;2;0;0;0;1;0;1;0;0;0 gfx90a [0x989f];3;32;32;32;30000;0;25;32;1;20;1;-2;0;0;1;0;1;0;2;0;0;0 gfx90a [0x989f];3;35;17;17;30000;0;15;35;1;29;1;1;0;0;1;0;1;0;2;1;0;0 gfx90a [0x989f];3;35;17;32;30000;0;20;35;1;1;1;0;-2;1;1;0;1;1;2;0;0;0 diff --git a/src/base/dbcsr_machine_posix.f90 b/src/base/dbcsr_machine_posix.f90 index cf40fc13d88..0d66707e7a8 100644 --- a/src/base/dbcsr_machine_posix.f90 +++ b/src/base/dbcsr_machine_posix.f90 @@ -17,7 +17,7 @@ PRIVATE PUBLIC :: m_flush, m_memory, & - m_hostnm, m_getcwd, m_getlog, m_getuid, m_getpid, m_getarg, & + m_hostnm, m_getcwd, m_getlog, m_getpid, m_getarg, & m_iargc, m_abort, m_chdir, m_mov, & m_memory_details, m_procrun @@ -325,20 +325,6 @@ SUBROUTINE m_getlog(user) END SUBROUTINE m_getlog -! ***************************************************************************** - SUBROUTINE m_getuid(uid) - INTEGER, INTENT(OUT) :: uid - - INTERFACE - FUNCTION getuid() BIND(C, name="getuid") RESULT(uid) - IMPORT - INTEGER(KIND=C_INT) :: uid - END FUNCTION - END INTERFACE - - uid = getuid() - END SUBROUTINE m_getuid - ! ***************************************************************************** SUBROUTINE m_getpid(pid) INTEGER, INTENT(OUT) :: pid