| 1889 | } |
| 1890 | |
| 1891 | void assertMatrixFitsInCpuMem(int numQubits, bool isDense, int isDistrib, int numEnvNodes, const char* caller) { |
| 1892 | |
| 1893 | // 'isDistrib' can be 0 (user-disabled, or matrix is a local type), 1 (user-forced) |
| 1894 | // or modeflag::USE_AUTO=-1 (automatic; the type can be distributed but the user has |
| 1895 | // not forced it). Currently, only distributed diagonal matrices are supported, so |
| 1896 | // isDistrib must be specifically 0 for dense matrices. |
| 1897 | if (isDense && isDistrib != 0) |
| 1898 | error_validationEncounteredUnsupportedDistributedDenseMatrix(); |
| 1899 | |
| 1900 | // attempt to fetch RAM, and simply return if we fail; if we unknowingly |
| 1901 | // didn't have enough RAM, then alloc validation will trigger later |
| 1902 | size_t memPerNode = 0; |
| 1903 | try { |
| 1904 | memPerNode = mem_tryGetLocalRamCapacityInBytes(); |
| 1905 | } catch(mem::COULD_NOT_QUERY_RAM e) { |
| 1906 | return; |
| 1907 | } |
| 1908 | |
| 1909 | // check whether matrix (considering if distributed) fits between node memory(s). |
| 1910 | // note this sets numMatrNodes=1 only when distribution is impossible/switched-off, |
| 1911 | // but not when it would later be automatically disabled. That's fine; the auto-deployer |
| 1912 | // will never disable distribution if the RAM can't store the entire matrix, so we |
| 1913 | // don't need to validate the auto-deployed-to-non-distributed scenario. We only need to |
| 1914 | // ensure that auto-deploying-to-distribution is permitted by memory capacity. Note too |
| 1915 | // that the distinction between (env.isDistributed) and (env.numNodes>1) is unimportant |
| 1916 | // for matrix structs because they never store communication buffers. |
| 1917 | int numMatrNodes = (isDistrib == 0)? 1 : numEnvNodes; |
| 1918 | bool matrFitsInMem = mem_canMatrixFitInMemory(numQubits, isDense, numMatrNodes, memPerNode); |
| 1919 | |
| 1920 | // specialise error message to whether matrix is distributed and dense or diag |
| 1921 | string msg = (isDense)? |
| 1922 | report::NEW_LOCAL_COMP_MATR_CANNOT_FIT_INTO_CPU_MEM : |
| 1923 | ((numMatrNodes == 1)? |
| 1924 | report::NEW_LOCAL_DIAG_MATR_CANNOT_FIT_INTO_CPU_MEM : |
| 1925 | report::NEW_DISTRIB_DIAG_MATR_CANNOT_FIT_INTO_CPU_MEM ); |
| 1926 | |
| 1927 | tokenSubs vars = { |
| 1928 | {"${NUM_QUBITS}", numQubits}, |
| 1929 | {"${QCOMP_BYTES}", sizeof(qcomp)}, |
| 1930 | {"${RAM_SIZE}", memPerNode}}; |
| 1931 | |
| 1932 | if (numMatrNodes > 1) { |
| 1933 | vars["${NUM_NODES}"] = numMatrNodes; |
| 1934 | vars["${NUM_QB_MINUS_LOG_NODES}"] = numQubits - logBase2(numMatrNodes); |
| 1935 | } |
| 1936 | |
| 1937 | /// @todo |
| 1938 | /// seek expensive node consensus in case of heterogeneous RAM - alas this may induce |
| 1939 | /// unnecessary slowdown (due to sync and broadcast) in applications allocating many |
| 1940 | /// small matrices in the heap. If this turns out to be the case, we could opt to |
| 1941 | /// enforce consensus only when the needed memory is large (e.g. >1GB) and ergo the |
| 1942 | /// chance of it fitting into some node RAM but not others isn't negligible. |
| 1943 | assertAllNodesAgreeThat(matrFitsInMem, msg, vars, caller); |
| 1944 | } |
| 1945 | |
| 1946 | void assertMatrixFitsInGpuMem(int numQubits, bool isDense, int isDistrib, int isGpu, int numEnvNodes, const char* caller) { |
| 1947 |
no test coverage detected