{ config , lib , cudaVersion }: # Type aliases # Gpu :: AttrSet # - See the documentation in ./gpus.nix. let inherit (lib) attrsets lists strings trivial versions; # Flags are determined based on your CUDA toolkit by default. You may benefit # from improved performance, reduced file size, or greater hardware support by # passing a configuration based on your specific GPU environment. # # config.cudaCapabilities :: List Capability # List of hardware generations to build. # E.g. [ "8.0" ] # Currently, the last item is considered the optional forward-compatibility arch, # but this may change in the future. # # config.cudaForwardCompat :: Bool # Whether to include the forward compatibility gencode (+PTX) # to support future GPU generations. # E.g. true # # Please see the accompanying documentation or https://github.com/NixOS/nixpkgs/pull/205351 # gpus :: List Gpu gpus = builtins.import ./gpus.nix; # isSupported :: Gpu -> Bool isSupported = gpu: let inherit (gpu) minCudaVersion maxCudaVersion; lowerBoundSatisfied = strings.versionAtLeast cudaVersion minCudaVersion; upperBoundSatisfied = (maxCudaVersion == null) || !(strings.versionOlder maxCudaVersion cudaVersion); in lowerBoundSatisfied && upperBoundSatisfied; # isDefault :: Gpu -> Bool isDefault = gpu: let inherit (gpu) dontDefaultAfter; newGpu = dontDefaultAfter == null; recentGpu = newGpu || strings.versionAtLeast dontDefaultAfter cudaVersion; in recentGpu; # supportedGpus :: List Gpu # GPUs which are supported by the provided CUDA version. supportedGpus = builtins.filter isSupported gpus; # defaultGpus :: List Gpu # GPUs which are supported by the provided CUDA version and we want to build for by default. defaultGpus = builtins.filter isDefault supportedGpus; # supportedCapabilities :: List Capability supportedCapabilities = lists.map (gpu: gpu.computeCapability) supportedGpus; # defaultCapabilities :: List Capability # The default capabilities to target, if not overridden by the user. defaultCapabilities = lists.map (gpu: gpu.computeCapability) defaultGpus; # cudaArchNameToVersions :: AttrSet String (List String) # Maps the name of a GPU architecture to different versions of that architecture. # For example, "Ampere" maps to [ "8.0" "8.6" "8.7" ]. cudaArchNameToVersions = lists.groupBy' (versions: gpu: versions ++ [ gpu.computeCapability ]) [ ] (gpu: gpu.archName) supportedGpus; # cudaComputeCapabilityToName :: AttrSet String String # Maps the version of a GPU architecture to the name of that architecture. # For example, "8.0" maps to "Ampere". cudaComputeCapabilityToName = builtins.listToAttrs ( lists.map (gpu: { name = gpu.computeCapability; value = gpu.archName; }) supportedGpus ); # dropDot :: String -> String dropDot = ver: builtins.replaceStrings [ "." ] [ "" ] ver; # archMapper :: String -> List String -> List String # Maps a feature across a list of architecture versions to produce a list of architectures. # For example, "sm" and [ "8.0" "8.6" "8.7" ] produces [ "sm_80" "sm_86" "sm_87" ]. archMapper = feat: lists.map (computeCapability: "${feat}_${dropDot computeCapability}"); # gencodeMapper :: String -> List String -> List String # Maps a feature across a list of architecture versions to produce a list of gencode arguments. # For example, "sm" and [ "8.0" "8.6" "8.7" ] produces [ "-gencode=arch=compute_80,code=sm_80" # "-gencode=arch=compute_86,code=sm_86" "-gencode=arch=compute_87,code=sm_87" ]. gencodeMapper = feat: lists.map ( computeCapability: "-gencode=arch=compute_${dropDot computeCapability},code=${feat}_${dropDot computeCapability}" ); formatCapabilities = { cudaCapabilities, enableForwardCompat ? true }: rec { inherit cudaCapabilities enableForwardCompat; # archNames :: List String # E.g. [ "Turing" "Ampere" ] archNames = lists.unique (builtins.map (cap: cudaComputeCapabilityToName.${cap} or (throw "missing cuda compute capability")) cudaCapabilities); # realArches :: List String # The real architectures are physical architectures supported by the CUDA version. # E.g. [ "sm_75" "sm_86" ] realArches = archMapper "sm" cudaCapabilities; # virtualArches :: List String # The virtual architectures are typically used for forward compatibility, when trying to support # an architecture newer than the CUDA version allows. # E.g. [ "compute_75" "compute_86" ] virtualArches = archMapper "compute" cudaCapabilities; # arches :: List String # By default, build for all supported architectures and forward compatibility via a virtual # architecture for the newest supported architecture. # E.g. [ "sm_75" "sm_86" "compute_86" ] arches = realArches ++ lists.optional enableForwardCompat (lists.last virtualArches); # gencode :: List String # A list of CUDA gencode arguments to pass to NVCC. # E.g. [ "-gencode=arch=compute_75,code=sm_75" ... "-gencode=arch=compute_86,code=compute_86" ] gencode = let base = gencodeMapper "sm" cudaCapabilities; forward = gencodeMapper "compute" [ (lists.last cudaCapabilities) ]; in base ++ lib.optionals enableForwardCompat forward; }; in # When changing names or formats: pause, validate, and update the assert assert (formatCapabilities { cudaCapabilities = [ "7.5" "8.6" ]; }) == { cudaCapabilities = [ "7.5" "8.6" ]; enableForwardCompat = true; archNames = [ "Turing" "Ampere" ]; realArches = [ "sm_75" "sm_86" ]; virtualArches = [ "compute_75" "compute_86" ]; arches = [ "sm_75" "sm_86" "compute_86" ]; gencode = [ "-gencode=arch=compute_75,code=sm_75" "-gencode=arch=compute_86,code=sm_86" "-gencode=arch=compute_86,code=compute_86" ]; }; { # formatCapabilities :: { cudaCapabilities: List Capability, cudaForwardCompat: Boolean } -> { ... } inherit formatCapabilities; # cudaArchNameToVersions :: String => String inherit cudaArchNameToVersions; # cudaComputeCapabilityToName :: String => String inherit cudaComputeCapabilityToName; # dropDot :: String -> String inherit dropDot; } // formatCapabilities { cudaCapabilities = config.cudaCapabilities or defaultCapabilities; enableForwardCompat = config.cudaForwardCompat or true; }