| 1944 | } |
| 1945 | |
| 1946 | void assertMatrixFitsInGpuMem(int numQubits, bool isDense, int isDistrib, int isGpu, int numEnvNodes, const char* caller) { |
| 1947 | |
| 1948 | // 'isDistrib' can be 0 (user-disabled, or matrix is a local type), 1 (user-forced) |
| 1949 | // or modeflag::USE_AUTO=-1 (automatic; the type can be distributed but the user has |
| 1950 | // not forced it). Currently, only distributed diagonal matrices are supported, so |
| 1951 | // isDistrib must be specifically 0 for dense matrices. |
| 1952 | if (isDense && isDistrib != 0) |
| 1953 | error_validationEncounteredUnsupportedDistributedDenseMatrix(); |
| 1954 | |
| 1955 | // 'isGpu' can be 0 (user-disabled, or matrix is a local type), 1 (user-forced), or |
| 1956 | // modeflag::USE_AUTO=-1 (automatic; the type can be GPU-accel but the user has not forced it). |
| 1957 | // Whenever GPU isn't forced, we do not need to check there's sufficient GPU memory, because |
| 1958 | // if there's insufficient, the auto-deployer will never choose it. So proceed only if GPU is forced. |
| 1959 | if (isGpu != 1) |
| 1960 | return; |
| 1961 | |
| 1962 | // we consult the current available local GPU memory (being more strict than is possible for RAM) |
| 1963 | size_t localCurrGpuMem = gpu_getCurrentAvailableMemoryInBytes(); |
| 1964 | |
| 1965 | // check whether matrix (considering if distributed) fits between node memory(s). |
| 1966 | // note this sets numMatrNodes=1 only when distribution is impossible/switched-off, |
| 1967 | // but not when it would later be automatically disabled. That's fine; the auto-deployer |
| 1968 | // will never disable distribution if the local GPU can't store the entire matrix, so we |
| 1969 | // don't need to validate the auto-deployed-to-non-distributed scenario. We only need to |
| 1970 | // ensure that auto-deploying-to-distribution is permitted by memory capacity. Note too |
| 1971 | // that the distinction between (env.isDistributed) and (env.numNodes>1) is unimportant |
| 1972 | // for matrix structs because they never store communication buffers. |
| 1973 | int numMatrNodes = (isDistrib == 0)? 1 : numEnvNodes; |
| 1974 | bool matrFitsInMem = mem_canMatrixFitInMemory(numQubits, isDense, numMatrNodes, localCurrGpuMem); |
| 1975 | |
| 1976 | // specialise error message to whether matrix is distributed and dense or diag |
| 1977 | string msg = (isDense)? |
| 1978 | report::NEW_LOCAL_COMP_MATR_CANNOT_FIT_INTO_GPU_MEM : |
| 1979 | ((numMatrNodes == 1)? |
| 1980 | report::NEW_LOCAL_DIAG_MATR_CANNOT_FIT_INTO_GPU_MEM : |
| 1981 | report::NEW_DISTRIB_DIAG_MATR_CANNOT_FIT_INTO_GPU_MEM ); |
| 1982 | |
| 1983 | tokenSubs vars = { |
| 1984 | {"${NUM_QUBITS}", numQubits}, |
| 1985 | {"${QCOMP_BYTES}", sizeof(qcomp)}, |
| 1986 | {"${VRAM_SIZE}", localCurrGpuMem}}; |
| 1987 | |
| 1988 | if (numMatrNodes > 1) { |
| 1989 | vars["${NUM_NODES}"] = numMatrNodes; |
| 1990 | vars["${NUM_QB_MINUS_LOG_NODES}"] = numQubits - logBase2(numMatrNodes); |
| 1991 | } |
| 1992 | |
| 1993 | /// @todo |
| 1994 | /// seek expensive node consensus in case of heterogeneous GPU hardware - alas this may |
| 1995 | /// induce unnecessary slowdown (due to sync and broadcast) in applications allocating many |
| 1996 | /// small matrices in the GPU. If this turns out to be the case, we could opt to |
| 1997 | /// enforce consensus only when the needed memory is large (e.g. >1GB) and ergo the |
| 1998 | /// chance of it fitting into some GPU's memory but not others isn't negligible. |
| 1999 | assertAllNodesAgreeThat(matrFitsInMem, msg, vars, caller); |
| 2000 | } |
| 2001 | |
| 2002 | void assertNewMatrixParamsAreValid(int numQubits, int useDistrib, int useGpu, int useMultithread, bool isDenseType, const char* caller) { |
| 2003 |
no test coverage detected