mirror of
https://github.com/paboyle/Grid.git
synced 2026-10-13 03:03:18 +01:00
Barrel shift -> all to all (x2) and distributed FFT work fully load balanced without redundant work. There is little more I can do now on FFT. Comms dominated and running distributed work dividing bandwidth optimal RingAllToAll /ccs/home/paboyle/ParallelIO/systems/Frontier/tests/core/Test_fft_prop --mpi 3.6.4.4 --grid 48.48.48.96 --accelerator-threads 8 --shm 4096 --shm-mpi 1 --device-mem 32000 --log Error,Warning,Message,Performance ************************************************* Benchmarking FFT of LatticeFermionD on plane wave ************************************************* Grid : Performance : 0.501524 s : FFT took 0.001311 s (transpose P=3) Grid : Performance : 0.501531 s : FFT pack 5.9e-05 s Grid : Performance : 0.501533 s : FFT alltoall 0.000828 s Grid : Performance : 0.501534 s : FFT reorder 0.000204 s Grid : Performance : 0.501535 s : FFT kernels 1e-05 s Grid : Performance : 0.501536 s : FFT unpack 5e-05 s Grid : Performance : 0.509992 s : FFT took 0.001829 s (transpose P=6) Grid : Performance : 0.510000 s : FFT pack 6e-05 s Grid : Performance : 0.510002 s : FFT alltoall 0.001436 s Grid : Performance : 0.510003 s : FFT reorder 0.000202 s Grid : Performance : 0.510005 s : FFT kernels 9e-06 s Grid : Performance : 0.510006 s : FFT unpack 5.3e-05 s Grid : Performance : 0.517690 s : FFT took 0.001599 s (transpose P=4) Grid : Performance : 0.517698 s : FFT pack 6e-05 s Grid : Performance : 0.517700 s : FFT alltoall 0.001258 s Grid : Performance : 0.517701 s : FFT reorder 0.0002 s Grid : Performance : 0.517702 s : FFT kernels 9e-06 s Grid : Performance : 0.517703 s : FFT unpack 4.9e-05 s Grid : Performance : 0.524858 s : FFT took 0.001561 s (transpose P=4) Grid : Performance : 0.524865 s : FFT pack 5.8e-05 s Grid : Performance : 0.524867 s : FFT alltoall 0.001213 s Grid : Performance : 0.524868 s : FFT reorder 0.000209 s Grid : Performance : 0.524869 s : FFT kernels 8e-06 s Grid : Performance : 0.524870 s : FFT unpack 4.9e-05 s ************************************************* FFT of [48 48 48 96] LatticeFermionD took 0.030916 s *************************************************
435 lines
14 KiB
C++
435 lines
14 KiB
C++
#include <Grid/GridCore.h>
|
|
|
|
NAMESPACE_BEGIN(Grid);
|
|
|
|
/*Allocation types, saying which pointer cache should be used*/
|
|
#define Cpu (0)
|
|
#define CpuHuge (1)
|
|
#define CpuSmall (2)
|
|
#define Acc (3)
|
|
#define AccHuge (4)
|
|
#define AccSmall (5)
|
|
#define Shared (6)
|
|
#define SharedHuge (7)
|
|
#define SharedSmall (8)
|
|
#undef GRID_MM_VERBOSE
|
|
uint64_t total_shared;
|
|
uint64_t total_device;
|
|
uint64_t total_host;;
|
|
|
|
#if defined(__has_feature)
|
|
#if __has_feature(leak_sanitizer)
|
|
#define ASAN_LEAK_CHECK
|
|
#endif
|
|
#endif
|
|
|
|
#ifdef ASAN_LEAK_CHECK
|
|
#include <sanitizer/asan_interface.h>
|
|
#include <sanitizer/common_interface_defs.h>
|
|
#include <sanitizer/lsan_interface.h>
|
|
#define LEAK_CHECK(A) { __lsan_do_recoverable_leak_check(); }
|
|
#else
|
|
#define LEAK_CHECK(A) { }
|
|
#endif
|
|
|
|
void MemoryManager::DisplayMallinfo(void)
|
|
{
|
|
#ifdef __linux__
|
|
struct mallinfo mi; // really want mallinfo2, but glibc version isn't uniform
|
|
|
|
mi = mallinfo();
|
|
|
|
std::cout << "MemoryManager: Total non-mmapped bytes (arena): "<< (size_t)mi.arena<<std::endl;
|
|
std::cout << "MemoryManager: # of free chunks (ordblks): "<< (size_t)mi.ordblks<<std::endl;
|
|
std::cout << "MemoryManager: # of free fastbin blocks (smblks): "<< (size_t)mi.smblks<<std::endl;
|
|
std::cout << "MemoryManager: # of mapped regions (hblks): "<< (size_t)mi.hblks<<std::endl;
|
|
std::cout << "MemoryManager: Bytes in mapped regions (hblkhd): "<< (size_t)mi.hblkhd<<std::endl;
|
|
std::cout << "MemoryManager: Max. total allocated space (usmblks): "<< (size_t)mi.usmblks<<std::endl;
|
|
std::cout << "MemoryManager: Free bytes held in fastbins (fsmblks): "<< (size_t)mi.fsmblks<<std::endl;
|
|
std::cout << "MemoryManager: Total allocated space (uordblks): "<< (size_t)mi.uordblks<<std::endl;
|
|
std::cout << "MemoryManager: Total free space (fordblks): "<< (size_t)mi.fordblks<<std::endl;
|
|
std::cout << "MemoryManager: Topmost releasable block (keepcost): "<< (size_t)mi.keepcost<<std::endl;
|
|
#endif
|
|
LEAK_CHECK();
|
|
|
|
}
|
|
|
|
void MemoryManager::PrintBytes(void)
|
|
{
|
|
std::cout << " MemoryManager : ------------------------------------ "<<std::endl;
|
|
std::cout << " MemoryManager : PrintBytes "<<std::endl;
|
|
std::cout << " MemoryManager : ------------------------------------ "<<std::endl;
|
|
std::cout << " MemoryManager : "<<(total_shared>>20)<<" shared Mbytes "<<std::endl;
|
|
std::cout << " MemoryManager : "<<(total_device>>20)<<" accelerator Mbytes "<<std::endl;
|
|
std::cout << " MemoryManager : "<<(total_host>>20) <<" cpu Mbytes "<<std::endl;
|
|
uint64_t cacheBytes;
|
|
cacheBytes = HostCacheBytes();
|
|
std::cout << " MemoryManager : "<<(cacheBytes>>20) <<" cpu alloc cache Mbytes "<<std::endl;
|
|
cacheBytes = DeviceCacheBytes();
|
|
std::cout << " MemoryManager : "<<(cacheBytes>>20) <<" acc alloc cache Mbytes "<<std::endl;
|
|
|
|
#ifdef GRID_CUDA
|
|
cuda_mem();
|
|
#endif
|
|
DisplayMallinfo();
|
|
}
|
|
|
|
uint64_t MemoryManager::DeviceCacheBytes() { return CacheBytes[Acc] + CacheBytes[AccHuge] + CacheBytes[AccSmall]; }
|
|
uint64_t MemoryManager::HostCacheBytes() { return CacheBytes[Cpu] + CacheBytes[CpuHuge] + CacheBytes[CpuSmall]; }
|
|
|
|
//////////////////////////////////////////////////////////////////////
|
|
// Data tables for recently freed pooiniter caches
|
|
//////////////////////////////////////////////////////////////////////
|
|
MemoryManager::AllocationCacheEntry MemoryManager::Entries[MemoryManager::NallocType][MemoryManager::NallocCacheMax];
|
|
int MemoryManager::Victim[MemoryManager::NallocType];
|
|
int MemoryManager::Ncache[MemoryManager::NallocType] = { 2, 0, 8, 8, 0, 16, 8, 0, 16 };
|
|
uint64_t MemoryManager::CacheBytes[MemoryManager::NallocType];
|
|
uint64_t MemoryManager::DeviceAllocCalls;
|
|
uint64_t MemoryManager::DeviceFreeCalls;
|
|
uint64_t MemoryManager::DeviceAllocBytes;
|
|
uint64_t MemoryManager::DeviceFreeBytes;
|
|
uint64_t MemoryManager::DeviceCacheHits;
|
|
|
|
void MemoryManager::PrintAllocCounts(void)
|
|
{
|
|
std::cout << GridLogMemory << "MemoryManager: device allocator calls: acceleratorAllocDevice "
|
|
<< DeviceAllocCalls <<" ("<< DeviceAllocBytes <<" bytes), acceleratorFreeDevice "
|
|
<< DeviceFreeCalls <<" ("<< DeviceFreeBytes <<" bytes), served from ring cache "
|
|
<< DeviceCacheHits << std::endl;
|
|
std::cout << GridLogMemory << "MemoryManager: view traffic: HostToDevice "
|
|
<< HostToDeviceXfer <<" transfers ("<< HostToDeviceBytes <<" bytes), DeviceToHost "
|
|
<< DeviceToHostXfer <<" transfers ("<< DeviceToHostBytes <<" bytes), evictions "
|
|
<< DeviceEvictions << std::endl;
|
|
}
|
|
void MemoryManager::Snapshot(const std::string &where)
|
|
{
|
|
if ( !GridLogMemory.isActive() ) return;
|
|
std::cout << GridLogMemory << "---------------- memory snapshot: "<< where <<" ----------------"<<std::endl;
|
|
PrintAllocCounts();
|
|
PrintBytes();
|
|
acceleratorMem();
|
|
}
|
|
//////////////////////////////////////////////////////////////////////
|
|
// Actual allocation and deallocation utils
|
|
//////////////////////////////////////////////////////////////////////
|
|
void *MemoryManager::AcceleratorAllocate(size_t bytes)
|
|
{
|
|
total_device+=bytes;
|
|
void *ptr = (void *) Lookup(bytes,Acc);
|
|
if ( ptr == (void *) NULL ) {
|
|
ptr = (void *) acceleratorAllocDevice(bytes);
|
|
DeviceAllocCalls++; DeviceAllocBytes+=bytes;
|
|
std::cout << GridLogMemory << "MemoryManager: acceleratorAllocDevice size "<< bytes
|
|
<<" AccPtr "<< std::hex << (uint64_t)ptr << std::dec << std::endl;
|
|
} else {
|
|
DeviceCacheHits++;
|
|
}
|
|
#ifdef GRID_MM_VERBOSE
|
|
std::cout <<"AcceleratorAllocate "<<std::endl;
|
|
PrintBytes();
|
|
#endif
|
|
return ptr;
|
|
}
|
|
void MemoryManager::AcceleratorFree (void *ptr,size_t bytes)
|
|
{
|
|
total_device-=bytes;
|
|
size_t freed_bytes=0;
|
|
void *__freeme = Insert(ptr,bytes,Acc,&freed_bytes);
|
|
if ( __freeme ) {
|
|
acceleratorFreeDevice(__freeme);
|
|
DeviceFreeCalls++; DeviceFreeBytes+=freed_bytes;
|
|
std::cout << GridLogMemory << "MemoryManager: acceleratorFreeDevice size "<< freed_bytes
|
|
<<" AccPtr "<< std::hex << (uint64_t)__freeme << std::dec << std::endl;
|
|
}
|
|
#ifdef GRID_MM_VERBOSE
|
|
std::cout <<"AcceleratorFree "<<std::endl;
|
|
PrintBytes();
|
|
#endif
|
|
}
|
|
void *MemoryManager::SharedAllocate(size_t bytes)
|
|
{
|
|
total_shared+=bytes;
|
|
void *ptr = (void *) Lookup(bytes,Shared);
|
|
if ( ptr == (void *) NULL ) {
|
|
ptr = (void *) acceleratorAllocShared(bytes);
|
|
}
|
|
#ifdef GRID_MM_VERBOSE
|
|
std::cout <<"SharedAllocate "<<std::endl;
|
|
PrintBytes();
|
|
#endif
|
|
return ptr;
|
|
}
|
|
void MemoryManager::SharedFree (void *ptr,size_t bytes)
|
|
{
|
|
total_shared-=bytes;
|
|
void *__freeme = Insert(ptr,bytes,Shared);
|
|
if ( __freeme ) {
|
|
acceleratorFreeShared(__freeme);
|
|
}
|
|
#ifdef GRID_MM_VERBOSE
|
|
std::cout <<"SharedFree "<<std::endl;
|
|
PrintBytes();
|
|
#endif
|
|
}
|
|
#ifdef GRID_UVM
|
|
void *MemoryManager::CpuAllocate(size_t bytes)
|
|
{
|
|
total_host+=bytes;
|
|
void *ptr = (void *) Lookup(bytes,Cpu);
|
|
if ( ptr == (void *) NULL ) {
|
|
ptr = (void *) acceleratorAllocShared(bytes);
|
|
}
|
|
#ifdef GRID_MM_VERBOSE
|
|
std::cout <<"CpuAllocate "<<std::endl;
|
|
PrintBytes();
|
|
#endif
|
|
return ptr;
|
|
}
|
|
void MemoryManager::CpuFree (void *_ptr,size_t bytes)
|
|
{
|
|
total_host-=bytes;
|
|
NotifyDeletion(_ptr);
|
|
void *__freeme = Insert(_ptr,bytes,Cpu);
|
|
if ( __freeme ) {
|
|
acceleratorFreeShared(__freeme);
|
|
}
|
|
#ifdef GRID_MM_VERBOSE
|
|
std::cout <<"CpuFree "<<std::endl;
|
|
PrintBytes();
|
|
#endif
|
|
}
|
|
#else
|
|
void *MemoryManager::CpuAllocate(size_t bytes)
|
|
{
|
|
total_host+=bytes;
|
|
void *ptr = (void *) Lookup(bytes,Cpu);
|
|
if ( ptr == (void *) NULL ) {
|
|
ptr = (void *) acceleratorAllocCpu(bytes);
|
|
}
|
|
#ifdef GRID_MM_VERBOSE
|
|
std::cout <<"CpuAllocate "<<std::endl;
|
|
PrintBytes();
|
|
#endif
|
|
return ptr;
|
|
}
|
|
void MemoryManager::CpuFree (void *_ptr,size_t bytes)
|
|
{
|
|
total_host-=bytes;
|
|
NotifyDeletion(_ptr);
|
|
void *__freeme = Insert(_ptr,bytes,Cpu);
|
|
if ( __freeme ) {
|
|
acceleratorFreeCpu(__freeme);
|
|
}
|
|
#ifdef GRID_MM_VERBOSE
|
|
std::cout <<"CpuFree "<<std::endl;
|
|
PrintBytes();
|
|
#endif
|
|
}
|
|
#endif
|
|
|
|
//////////////////////////////////////////
|
|
// call only once
|
|
//////////////////////////////////////////
|
|
void MemoryManager::Init(void)
|
|
{
|
|
|
|
char * str;
|
|
int Nc;
|
|
|
|
str= getenv("GRID_ALLOC_NCACHE_LARGE");
|
|
if ( str ) {
|
|
Nc = atoi(str);
|
|
if ( (Nc>=0) && (Nc < NallocCacheMax)) {
|
|
Ncache[Cpu]=Nc;
|
|
Ncache[Acc]=Nc;
|
|
Ncache[Shared]=Nc;
|
|
}
|
|
}
|
|
|
|
str= getenv("GRID_ALLOC_NCACHE_HUGE");
|
|
if ( str ) {
|
|
Nc = atoi(str);
|
|
if ( (Nc>=0) && (Nc < NallocCacheMax)) {
|
|
Ncache[CpuHuge]=Nc;
|
|
Ncache[AccHuge]=Nc;
|
|
Ncache[SharedHuge]=Nc;
|
|
}
|
|
}
|
|
|
|
str= getenv("GRID_ALLOC_NCACHE_SMALL");
|
|
if ( str ) {
|
|
Nc = atoi(str);
|
|
if ( (Nc>=0) && (Nc < NallocCacheMax)) {
|
|
Ncache[CpuSmall]=Nc;
|
|
Ncache[AccSmall]=Nc;
|
|
Ncache[SharedSmall]=Nc;
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
void MemoryManager::InitMessage(void) {
|
|
|
|
#ifndef GRID_UVM
|
|
std::cout << GridLogMessage << "MemoryManager Cache "<< MemoryManager::DeviceMaxBytes <<" bytes "<<std::endl;
|
|
#endif
|
|
|
|
std::cout << GridLogMessage<< "MemoryManager::Init() setting up"<<std::endl;
|
|
#ifdef ALLOCATION_CACHE
|
|
std::cout << GridLogMessage<< "MemoryManager::Init() cache pool for recent host allocations: SMALL "<<Ncache[CpuSmall]<<" LARGE "<<Ncache[Cpu]<<" HUGE "<<Ncache[CpuHuge]<<std::endl;
|
|
std::cout << GridLogMessage<< "MemoryManager::Init() cache pool for recent device allocations: SMALL "<<Ncache[AccSmall]<<" LARGE "<<Ncache[Acc]<<" Huge "<<Ncache[AccHuge]<<std::endl;
|
|
std::cout << GridLogMessage<< "MemoryManager::Init() cache pool for recent shared allocations: SMALL "<<Ncache[SharedSmall]<<" LARGE "<<Ncache[Shared]<<" Huge "<<Ncache[SharedHuge]<<std::endl;
|
|
#endif
|
|
|
|
#ifdef GRID_UVM
|
|
std::cout << GridLogMessage<< "MemoryManager::Init() Unified memory space"<<std::endl;
|
|
#ifdef GRID_CUDA
|
|
std::cout << GridLogMessage<< "MemoryManager::Init() Using cudaMallocManaged"<<std::endl;
|
|
#endif
|
|
#ifdef GRID_HIP
|
|
std::cout << GridLogMessage<< "MemoryManager::Init() Using hipMallocManaged"<<std::endl;
|
|
#endif
|
|
#ifdef GRID_SYCL
|
|
std::cout << GridLogMessage<< "MemoryManager::Init() Using SYCL malloc_shared"<<std::endl;
|
|
#endif
|
|
#else
|
|
std::cout << GridLogMessage<< "MemoryManager::Init() Non unified: Caching accelerator data in dedicated memory"<<std::endl;
|
|
#ifdef GRID_CUDA
|
|
std::cout << GridLogMessage<< "MemoryManager::Init() Using cudaMalloc"<<std::endl;
|
|
#endif
|
|
#ifdef GRID_HIP
|
|
std::cout << GridLogMessage<< "MemoryManager::Init() Using hipMalloc"<<std::endl;
|
|
#endif
|
|
#ifdef GRID_SYCL
|
|
std::cout << GridLogMessage<< "MemoryManager::Init() Using SYCL malloc_device"<<std::endl;
|
|
#endif
|
|
#endif
|
|
|
|
}
|
|
void *MemoryManager::Insert(void *ptr,size_t bytes,int type,size_t *freed)
|
|
{
|
|
#ifdef ALLOCATION_CACHE
|
|
int cache;
|
|
if (bytes < GRID_ALLOC_SMALL_LIMIT) cache = type + 2;
|
|
else if (bytes >= GRID_ALLOC_HUGE_LIMIT) cache = type + 1;
|
|
else cache = type;
|
|
|
|
return Insert(ptr,bytes,Entries[cache],Ncache[cache],Victim[cache],CacheBytes[cache],freed);
|
|
#else
|
|
return ptr;
|
|
#endif
|
|
}
|
|
void MemoryManager::DropCache(void)
|
|
{
|
|
// Release every block held in the allocation caches (the "recent
|
|
// allocations" pools: Small/Large/Huge x Cpu/Acc/Shared). Blocks in these
|
|
// pools are freed from the caller's point of view but still occupy memory;
|
|
// after a large setup phase (SUMMA scratch, coarsening temporaries) they can
|
|
// hold gigabytes of device memory that hipMalloc then cannot get. Each pool
|
|
// is freed with the call that pairs with its allocator.
|
|
for(int sz=0;sz<3;sz++) {
|
|
int t;
|
|
t = Acc+sz;
|
|
for(int e=0;e<Ncache[t];e++) {
|
|
if( Entries[t][e].valid ) {
|
|
acceleratorFreeDevice(Entries[t][e].address);
|
|
Entries[t][e].valid = 0; Entries[t][e].bytes = 0; Entries[t][e].address = NULL;
|
|
}
|
|
}
|
|
CacheBytes[t]=0; Victim[t]=0;
|
|
t = Shared+sz;
|
|
for(int e=0;e<Ncache[t];e++) {
|
|
if( Entries[t][e].valid ) {
|
|
acceleratorFreeShared(Entries[t][e].address);
|
|
Entries[t][e].valid = 0; Entries[t][e].bytes = 0; Entries[t][e].address = NULL;
|
|
}
|
|
}
|
|
CacheBytes[t]=0; Victim[t]=0;
|
|
t = Cpu+sz;
|
|
for(int e=0;e<Ncache[t];e++) {
|
|
if( Entries[t][e].valid ) {
|
|
#ifdef GRID_UVM
|
|
acceleratorFreeShared(Entries[t][e].address);
|
|
#else
|
|
acceleratorFreeCpu(Entries[t][e].address);
|
|
#endif
|
|
Entries[t][e].valid = 0; Entries[t][e].bytes = 0; Entries[t][e].address = NULL;
|
|
}
|
|
}
|
|
CacheBytes[t]=0; Victim[t]=0;
|
|
}
|
|
}
|
|
|
|
void *MemoryManager::Insert(void *ptr,size_t bytes,AllocationCacheEntry *entries,int ncache,int &victim, uint64_t &cacheBytes,size_t *freed)
|
|
{
|
|
#ifdef GRID_OMP
|
|
GRID_ASSERT(omp_in_parallel()==0);
|
|
#endif
|
|
|
|
if (ncache == 0) { if(freed) *freed = bytes; return ptr; } // uncached: the incoming block is the one freed
|
|
|
|
void * ret = NULL;
|
|
int v = -1;
|
|
|
|
for(int e=0;e<ncache;e++) {
|
|
if ( entries[e].valid==0 ) {
|
|
v=e;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if ( v==-1 ) {
|
|
v=victim;
|
|
victim = (victim+1)%ncache;
|
|
}
|
|
|
|
if ( entries[v].valid ) {
|
|
ret = entries[v].address;
|
|
if ( freed ) *freed = entries[v].bytes; // the DISPLACED block is what actually gets freed
|
|
cacheBytes -= entries[v].bytes;
|
|
entries[v].valid = 0;
|
|
entries[v].address = NULL;
|
|
entries[v].bytes = 0;
|
|
}
|
|
|
|
entries[v].address=ptr;
|
|
entries[v].bytes =bytes;
|
|
entries[v].valid =1;
|
|
cacheBytes += bytes;
|
|
|
|
return ret;
|
|
}
|
|
|
|
void *MemoryManager::Lookup(size_t bytes,int type)
|
|
{
|
|
#ifdef ALLOCATION_CACHE
|
|
int cache;
|
|
if (bytes < GRID_ALLOC_SMALL_LIMIT) cache = type + 2;
|
|
else if (bytes >= GRID_ALLOC_HUGE_LIMIT) cache = type + 1;
|
|
else cache = type;
|
|
|
|
return Lookup(bytes,Entries[cache],Ncache[cache],CacheBytes[cache]);
|
|
#else
|
|
return NULL;
|
|
#endif
|
|
}
|
|
|
|
void *MemoryManager::Lookup(size_t bytes,AllocationCacheEntry *entries,int ncache,uint64_t & cacheBytes)
|
|
{
|
|
#ifdef GRID_OMP
|
|
GRID_ASSERT(omp_in_parallel()==0);
|
|
#endif
|
|
for(int e=0;e<ncache;e++){
|
|
if ( entries[e].valid && ( entries[e].bytes == bytes ) ) {
|
|
entries[e].valid = 0;
|
|
cacheBytes -= entries[e].bytes;
|
|
return entries[e].address;
|
|
}
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
|
|
NAMESPACE_END(Grid);
|
|
|