diff options
| author | ruki <[email protected]> | 2019-06-02 20:07:04 +0800 |
|---|---|---|
| committer | GitHub <[email protected]> | 2019-06-02 20:07:04 +0800 |
| commit | adf7265401df3259ff90868cfcc9baaad66d172d (patch) | |
| tree | 2782ee1cedad5c9dd4a25fa886f5fd49c883962f | |
| parent | 78f4fa1228d508f4aceecfe03fd233478f56da54 (diff) | |
| parent | 117f22a385f0118eac2aa6ef8fedf4b29062e973 (diff) | |
Merge pull request #430 from OpportunityLiu/dev
支持 nvcc -gencode 选项
| -rw-r--r-- | .gitignore | 1 | ||||
| -rw-r--r-- | tests/projects/cuda/console/xmake.lua | 14 | ||||
| -rw-r--r-- | xmake/includes/add_cugencodes.lua | 148 | ||||
| -rw-r--r-- | xmake/modules/lib/detect/find_cudadevices.lua | 271 | ||||
| -rw-r--r-- | xmake/scripts/find_cudadevices.cu | 207 |
5 files changed, 633 insertions, 8 deletions
diff --git a/.gitignore b/.gitignore index abfbe8941..9dd2d123e 100644 --- a/.gitignore +++ b/.gitignore @@ -41,3 +41,4 @@ core/.config.mak winenv xmake-ppa !xmake/actions/build +.vscode/*
\ No newline at end of file diff --git a/tests/projects/cuda/console/xmake.lua b/tests/projects/cuda/console/xmake.lua index b50beb03c..430ad7b6d 100644 --- a/tests/projects/cuda/console/xmake.lua +++ b/tests/projects/cuda/console/xmake.lua @@ -1,3 +1,6 @@ + +includes('add_cugencodes.lua') + -- define target target("cuda_console") @@ -11,14 +14,9 @@ target("cuda_console") add_files("src/*.cu") -- generate SASS code for each SM architecture - for _, sm in ipairs({"30", "35", "37", "50", "52", "60", "61", "70"}) do - add_cuflags("-gencode arch=compute_" .. sm .. ",code=sm_" .. sm) - add_ldflags("-gencode arch=compute_" .. sm .. ",code=sm_" .. sm) - end + add_cugencodes("sm_30", "sm_35", "sm_37", "sm_50", "sm_52", "sm_60", "sm_61", "sm_70") -- generate PTX code from the highest SM architecture to guarantee forward-compatibility - sm = "70" - add_cuflags("-gencode arch=compute_" .. sm .. ",code=compute_" .. sm) - add_ldflags("-gencode arch=compute_" .. sm .. ",code=compute_" .. sm) - + add_cugencodes("compute_70") + diff --git a/xmake/includes/add_cugencodes.lua b/xmake/includes/add_cugencodes.lua new file mode 100644 index 000000000..574c7bcb9 --- /dev/null +++ b/xmake/includes/add_cugencodes.lua @@ -0,0 +1,148 @@ +--!A cross-platform build utility based on Lua +-- +-- Licensed under the Apache License, Version 2.0 (the "License"); +-- you may not use this file except in compliance with the License. +-- You may obtain a copy of the License at +-- +-- http://www.apache.org/licenses/LICENSE-2.0 +-- +-- Unless required by applicable law or agreed to in writing, software +-- distributed under the License is distributed on an "AS IS" BASIS, +-- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +-- See the License for the specific language governing permissions and +-- limitations under the License. +-- +-- Copyright (C) 2015 - 2019, TBOOX Open Source Group. +-- +-- @author OpportunityLiu +-- @file add_cugencodes.lua +-- + +-- add cuda `-gencode` flags to target +-- +-- the gpu arch format syntax +-- - compute_xx --> `-gencode arch=compute_xx,code=compute_xx` +-- - sm_xx --> `-gencode arch=compute_xx,code=sm_xx` +-- - sm_xx,sm_yy --> `-gencode arch=compute_xx,code=[sm_xx,sm_yy]` +-- - compute_xx,sm_yy --> `-gencode arch=compute_xx,code=sm_yy` +-- - compute_xx,sm_yy,sm_zz --> `-gencode arch=compute_xx,code=[sm_yy,sm_zz]` +-- - native --> match the fastest cuda device on current host, +-- eg. for a Tesla P100, `-gencode arch=compute_60,code=sm_60` will be added, +-- if no available device is found, no `-gencode` flags will be added +-- @seealso xmake/modules/lib/detect/find_cudadevices +-- +-- e.g. +-- includes("add_cugencodes.lua") +-- target("test") +-- set_kind("binary") +-- add_files("src/*.cu") +-- add_cugencodes("native", "compute_50,sm_50", "compute_70") +-- + + +-- define rule +rule("cuda.add_gencodes") + before_load(function (target) + + local function set (list) + local result = {} + for _, l in ipairs(list) do result[l] = true end + return result + end + + -- sm_20 and compute_20 is supported until CUDA 8 + local knownVArchs = set { 20, 30, 32, 35, 37, 50, 52, 53, 60, 61, 62, 70, 72, 75, } + local knownRArchs = set { 20, 30, 32, 35, 37, 50, 52, 53, 60, 61, 62, 70, 72, 75, } + + local function nf_cugencode(archs) + if type(archs) ~= 'string' then + return nil + end + + archs = archs:trim():lower() + + if archs == 'native' then + import("lib.detect.find_cudadevices") + local device = find_cudadevices({ skip_compute_mode_prohibited = true, order_by_flops = true })[1] + if device then + return nf_cugencode('sm_' .. device.major .. device.minor) + end + return nil + end + + local vArch = nil + local rArchs = {} + + local function parse_arch(value, prefix, knowList) + if not value:startswith(prefix) then + return nil + end + local arch = tonumber(value:sub(#prefix + 1)) or tonumber(value:sub(#prefix + 2)) + if arch == nil then + raise("Unknown architecture: " .. value) + end + if not knowList[arch] then + if arch <= table.maxn(knowList) then + raise("Unknown architecture: " .. prefix .. "_" .. arch) + else + utils.warning("Unknown architecture: " .. prefix .. "_" .. arch) + end + end + return arch + end + + for _, v in ipairs(archs:split(',')) do + local arch = v:trim() + local tempRArch = parse_arch(arch, 'sm', knownRArchs) + if tempRArch then + table.insert(rArchs, tempRArch) + end + + local tempVArch = parse_arch(arch, 'compute', knownVArchs) + if tempVArch then + if vArch ~= nil then + raise("More than one virtual architecture is defined in one gpu gencode option: compute_" .. vArch .. " and compute_" .. tempVArch) + end + vArch = tempVArch + end + + if not (tempRArch or tempVArch) then + raise("Unknown architecture: " .. arch) + end + end + + if vArch == nil and #rArchs == 0 then + return nil + end + + if #rArchs == 0 then + return '-gencode arch=compute_' .. vArch .. ',code=compute_' .. vArch + end + + rArchs = table.unique(rArchs) + vArch = vArch or math.min(unpack(rArchs)) + + if #rArchs == 1 then + return '-gencode arch=compute_' .. vArch .. ',code=sm_' .. rArchs[1] + else + return '-gencode arch=compute_' .. vArch .. ',code=[sm_' .. table.concat(rArchs, ',sm_') .. ']' + end + end + + for _, v in ipairs(target:values("cuda.gencode")) do + local flag = nf_cugencode(v) + if flag then + target:add('cuflags', flag) + target:add('ldflags', flag) + end + end + end) +rule_end() + +-- add cuda gencode to target +function add_cugencodes(...) + -- apply rule + add_rules("cuda.add_gencodes") + add_values("cuda.gencode", ...) +end + diff --git a/xmake/modules/lib/detect/find_cudadevices.lua b/xmake/modules/lib/detect/find_cudadevices.lua new file mode 100644 index 000000000..c009319aa --- /dev/null +++ b/xmake/modules/lib/detect/find_cudadevices.lua @@ -0,0 +1,271 @@ +--!A cross-platform build utility based on Lua +-- +-- Licensed under the Apache License, Version 2.0 (the "License"); +-- you may not use this file except in compliance with the License. +-- You may obtain a copy of the License at +-- +-- http://www.apache.org/licenses/LICENSE-2.0 +-- +-- Unless required by applicable law or agreed to in writing, software +-- distributed under the License is distributed on an "AS IS" BASIS, +-- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +-- See the License for the specific language governing permissions and +-- limitations under the License. +-- +-- Copyright (C) 2015 - 2019, TBOOX Open Source Group. +-- +-- @author OpportunityLiu +-- @file find_cudadevices.lua +-- + +-- imports +import("core.base.option") +import("core.platform.platform") +import("lib.detect.cache") + +-- a magic string to filter output +local _PRINT_SUFFIX = "<find_cudadevices>" + +-- filter stdout and stderr with _PRINT_SUFFIX +function _get_lines(str) + local result = {} + for _, l in ipairs(str:split('\n')) do + if l:startswith(_PRINT_SUFFIX) then + table.insert(result, l:sub(#_PRINT_SUFFIX + 1)) + end + end + return result +end + + +-- parse a single value +-- format: +-- 1. a number: `2048` +-- 2. an array: `(65536, 2048, 2048)` +-- 3. bool value: `true` or `false` +-- 4. string: `"string"` +function _parse_value(value) + local num = tonumber(value) + if num then return num end + + if value:lower() == "true" then return true end + if value:lower() == "false" then return false end + + if value:startswith('"') and value:endswith('"') then + return value:sub(2, -2) + end + + if value:startswith('(') and value:endswith(')') then + local values = value:sub(2, -2):split(',') + local result = {} + for _, v in ipairs(values) do + table.insert(result, _parse_value(v:trim())) + end + return result + end + + raise("don't know how to parse value: %s", value) +end + + +-- parse single line +-- format: +-- key = value +function _parse_line(line, device) + local key = line:match("%s+(%g+) = .+") + local value = line:match("%s+%g+ = (.+)") + if key and value then + key = key:trim() + value = value:trim() + assert(not device[key], 'duplicate key: ' .. key) + device[key] = _parse_value(value) + end +end + + +-- parse filtered lines +function _parse_result(lines, verbose) + if #lines == 0 then + -- not a failure, returns {} rather than nil + utils.warning("no cuda devices was found") + return {} + end + + local devices = {} + local currentDevice = nil + for _, l in ipairs(lines) do + if verbose then + cprint("${dim}> %s", l) + end + local devId = tonumber(l:match("%s*DEVICE #(%d+)")) + if devId then + currentDevice = { ['$id'] = devId } + table.insert(devices, currentDevice) + elseif currentDevice then + _parse_line(l, currentDevice) + end + end + return devices +end + + +-- find devices +function _find_devices(verbose) + local nvcc = platform.tool("cu") + if nvcc == nil then + raise('nvcc not found') + end + + if verbose then + cprint("${dim}checking for cuda devices") + end + + local sourcefile = path.join(os.programdir(), 'scripts', 'find_cudadevices.cu') + local outfile = os.tmpfile() + + local compileerrors = nil + local results, errors = try { function () + return os.iorunv(nvcc, { sourcefile, '-run', '-o', outfile , '-DPRINT_SUFFIX="' .. _PRINT_SUFFIX .. '"' }) + end, catch {function (errs) compileerrors = tostring(errs) end} } + + if compileerrors ~=nil then + if not option.get("diagnosis") then + compileerrors = compileerrors:split('\n')[1] + end + utils.warning("failed to find cuda devices: " .. compileerrors) + return nil + end + + -- clean up + os.rm(outfile) + os.rm(outfile .. '.*') + + local results_lines = _get_lines(results) + local errors_lines = _get_lines(errors) + + if #errors_lines ~= 0 then + utils.warning("failed to find cuda devices: " .. table.concat(errors_lines, '\n')) + return nil + end + + -- print raw result only with -D flags + local devices = _parse_result(results_lines, option.get("diagnosis")) + if verbose then + for _, v in ipairs(devices) do + cprint("${dim}> found device #%d: ${green bright}%s${reset dim} with compute ${bright}%d.%d${reset dim} capability", v['$id'], v.name, v.major, v.minor) + end + end + return devices +end + + +-- get devices array form cache or via _find_devices +function _get_devices(opt) + -- init cachekey + local cachekey = "find_cudadevices" + if opt.cachekey then + cachekey = cachekey .. "_" .. opt.cachekey + end + + -- check cache + local cachedata = cache.load(cachekey) + if cachedata.succeed and not opt.force then + return cachedata.data + end + + local verbose = opt.verbose or option.get("verbose") or option.get("diagnosis") + local devices = _find_devices(verbose) + + if devices then + cachedata = { succeed = true, data = devices } + else + cachedata = { succeed = false } + devices = {} + end + + -- fill cache + cache.save(cachekey, cachedata) + return devices +end + +function _skip_compute_mode_prohibited(devices) + local results = {} + local cudaComputeModeProhibited = 2 + for _, dev in ipairs(devices) do + if dev.computeMode ~= cudaComputeModeProhibited then + table.insert(results, dev) + end + end + return results +end + +function _min_sm_arch(devices, min_sm_arch) + local results = {} + for _, dev in ipairs(devices) do + if dev.major * 10 + dev.minor >= min_sm_arch then + table.insert(results, dev) + end + end + return results +end + +function _order_by_flops(devices) + local nGpuArchCoresPerSM = { + [30] = 192 + , [32] = 192 + , [35] = 192 + , [37] = 192 + , [50] = 128 + , [52] = 128 + , [53] = 128 + , [60] = 64 + , [61] = 128 + , [62] = 128 + , [70] = 64 + , [72] = 64 + , [75] = 64 + } + + for _, dev in ipairs(devices) do + local sm_per_multiproc = 0 + if dev.major == 9999 and dev.minor == 9999 then + sm_per_multiproc = 1 + else + sm_per_multiproc = nGpuArchCoresPerSM[dev.major * 10 + dev.minor] or 64; + end + dev['$flops'] = dev.multiProcessorCount * sm_per_multiproc * dev.clockRate + end + + table.sort(devices, function (a,b) return a['$flops'] > b['$flops'] end) + return devices +end + +-- find cuda devices of the host +-- +-- @param opt the options +-- e.g. { verbose = false, force = false, cachekey = "xxxx", min_sm_arch = 35, skip_compute_mode_prohibited = false, order_by_flops = true } +-- +-- @return { { ['$id'] = 0, name = "GeForce GTX 960M", major = 5, minor = 0, ... }, ... } +-- for all keys, see https://docs.nvidia.com/cuda/cuda-runtime-api/structcudaDeviceProp.html#structcudaDeviceProp +-- keys might be differ as your cuda version varies +-- +function main(opt) + -- init options + opt = opt or {} + + -- get devices + local devices = _get_devices(opt) + + -- apply filters + if opt.min_sm_arch then + devices = _min_sm_arch(devices, opt.min_sm_arch) + end + if opt.skip_compute_mode_prohibited then + devices = _skip_compute_mode_prohibited(devices) + end + if opt.order_by_flops then + devices = _order_by_flops(devices) + end + + return devices +end diff --git a/xmake/scripts/find_cudadevices.cu b/xmake/scripts/find_cudadevices.cu new file mode 100644 index 000000000..56d64ff2b --- /dev/null +++ b/xmake/scripts/find_cudadevices.cu @@ -0,0 +1,207 @@ +#include <cuda_runtime.h> +#include <stdio.h> +#include <stdlib.h> + +#ifndef PRINT_SUFFIX +#define PRINT_SUFFIX "<find_cudadevices>" +#endif + +#define MY_CUDA_VER (__CUDACC_VER_MAJOR__ * 100 + __CUDACC_VER_MINOR__) + +inline void check(cudaError_t result) +{ + if (result) + { + fprintf(stderr, PRINT_SUFFIX "%s (%s)", cudaGetErrorName(result), cudaGetErrorString(result)); + cudaDeviceReset(); + // Make sure we call CUDA Device Reset before exiting + exit(0); + } +} + +inline void print_value(size_t value) +{ + // in case we don't have '%zu' + printf("%llu", (unsigned long long)value); +} + +inline void print_value(bool value) +{ + printf(value ? "true" : "false"); +} + +inline void print_value(int value) +{ + printf("%d", value); +} + +template <typename T, size_t len> +inline void print_value(const T (&value)[len]) +{ + printf("("); + for (size_t i = 0; i < len - 1; i++) + { + print_value(value[i]); + printf(", "); + } + print_value(value[len - 1]); + printf(")"); +} + +inline void print_value(unsigned int value) +{ + printf("%u", value); +} + +inline void print_value(const void *value) +{ + printf("\"%s\"", (const char *)value); +} + +template <size_t len> +inline void print_value(const char (&value)[len]) +{ + printf("\""); + for (size_t i = 0; i < len; i++) + printf("%02hhx", value[i]); + printf("\""); +} + +template <> +inline void print_value<16>(const char (&value)[16]) +{ + // speicalized for uuid + printf("\"%02hhx%02hhx%02hhx%02hhx-%02hhx%02hhx-%02hhx%02hhx-%02hhx%02hhx-%02hhx%02hhx%02hhx%02hhx%02hhx%02hhx\"", + value[0], value[1], value[2], value[3], + value[4], value[5], value[6], value[7], + value[8], value[9], value[10], value[11], + value[12], value[13], value[14], value[15]); +} + +#if MY_CUDA_VER >= 1000 +inline void print_value(const cudaUUID_t &value) +{ + print_value(value.bytes); +} +#endif + +template <typename T> +inline void print_property(const char *name, const T &value) +{ + printf(PRINT_SUFFIX " %s = ", name); + print_value(value); + printf("\n"); +} + +inline void print_device(int id) +{ + cudaDeviceProp deviceProp; + check(cudaGetDeviceProperties(&deviceProp, id)); + +#define PRINT_PROPERTY(name) print_property(#name, deviceProp.name) +#define PRINT_BOOL_PROPERTY(name) print_property(#name, static_cast<bool>(deviceProp.name)) +#define PRINT_STR_PROPERTY(name) print_property(#name, static_cast<const void *>(deviceProp.name)) + // cuda 8.0 + PRINT_STR_PROPERTY(name); + PRINT_PROPERTY(totalGlobalMem); + PRINT_PROPERTY(sharedMemPerBlock); + PRINT_PROPERTY(regsPerBlock); + PRINT_PROPERTY(warpSize); + PRINT_PROPERTY(memPitch); + PRINT_PROPERTY(maxThreadsPerBlock); + PRINT_PROPERTY(maxThreadsDim); + PRINT_PROPERTY(maxGridSize); + PRINT_PROPERTY(clockRate); + PRINT_PROPERTY(totalConstMem); + PRINT_PROPERTY(major); + PRINT_PROPERTY(minor); + PRINT_PROPERTY(textureAlignment); + PRINT_PROPERTY(texturePitchAlignment); + PRINT_BOOL_PROPERTY(deviceOverlap); + PRINT_PROPERTY(multiProcessorCount); + PRINT_BOOL_PROPERTY(kernelExecTimeoutEnabled); + PRINT_BOOL_PROPERTY(integrated); + PRINT_BOOL_PROPERTY(canMapHostMemory); + PRINT_PROPERTY(computeMode); + PRINT_PROPERTY(maxTexture1D); + PRINT_PROPERTY(maxTexture1DMipmap); + PRINT_PROPERTY(maxTexture1DLinear); + PRINT_PROPERTY(maxTexture2D); + PRINT_PROPERTY(maxTexture2DMipmap); + PRINT_PROPERTY(maxTexture2DLinear); + PRINT_PROPERTY(maxTexture2DGather); + PRINT_PROPERTY(maxTexture3D); + PRINT_PROPERTY(maxTexture3DAlt); + PRINT_PROPERTY(maxTextureCubemap); + PRINT_PROPERTY(maxTexture1DLayered); + PRINT_PROPERTY(maxTexture2DLayered); + PRINT_PROPERTY(maxTextureCubemapLayered); + PRINT_PROPERTY(maxSurface1D); + PRINT_PROPERTY(maxSurface2D); + PRINT_PROPERTY(maxSurface3D); + PRINT_PROPERTY(maxSurface1DLayered); + PRINT_PROPERTY(maxSurface2DLayered); + PRINT_PROPERTY(maxSurfaceCubemap); + PRINT_PROPERTY(maxSurfaceCubemapLayered); + PRINT_PROPERTY(surfaceAlignment); + PRINT_BOOL_PROPERTY(concurrentKernels); + PRINT_BOOL_PROPERTY(ECCEnabled); + PRINT_PROPERTY(pciBusID); + PRINT_PROPERTY(pciDeviceID); + PRINT_PROPERTY(pciDomainID); + PRINT_BOOL_PROPERTY(tccDriver); + PRINT_PROPERTY(asyncEngineCount); + PRINT_BOOL_PROPERTY(unifiedAddressing); + PRINT_PROPERTY(memoryClockRate); + PRINT_PROPERTY(memoryBusWidth); + PRINT_PROPERTY(l2CacheSize); + PRINT_PROPERTY(maxThreadsPerMultiProcessor); + PRINT_BOOL_PROPERTY(streamPrioritiesSupported); + PRINT_BOOL_PROPERTY(globalL1CacheSupported); + PRINT_BOOL_PROPERTY(localL1CacheSupported); + PRINT_PROPERTY(sharedMemPerMultiprocessor); + PRINT_PROPERTY(regsPerMultiprocessor); + PRINT_BOOL_PROPERTY(isMultiGpuBoard); + PRINT_PROPERTY(multiGpuBoardGroupID); + PRINT_PROPERTY(singleToDoublePrecisionPerfRatio); + PRINT_BOOL_PROPERTY(pageableMemoryAccess); + PRINT_BOOL_PROPERTY(concurrentManagedAccess); + PRINT_BOOL_PROPERTY(managedMemory); + +#if MY_CUDA_VER >= 900 + // Added in cuda 9.0 + PRINT_BOOL_PROPERTY(computePreemptionSupported); + PRINT_BOOL_PROPERTY(canUseHostPointerForRegisteredMem); + PRINT_BOOL_PROPERTY(cooperativeLaunch); + PRINT_BOOL_PROPERTY(cooperativeMultiDeviceLaunch); + PRINT_PROPERTY(sharedMemPerBlockOptin); +#endif + +#if MY_CUDA_VER >= 902 + // Added in cuda 9.2 + PRINT_BOOL_PROPERTY(pageableMemoryAccessUsesHostPageTables); + PRINT_BOOL_PROPERTY(directManagedMemAccessFromHost); +#endif + +#if MY_CUDA_VER >= 1000 + // Added in cuda 10.0 + PRINT_PROPERTY(uuid); + PRINT_PROPERTY(luid); + PRINT_PROPERTY(luidDeviceNodeMask); +#endif +} + +int main(int argc, char *argv[]) +{ + printf("\n"); + fprintf(stderr, "\n"); + + int count = 0; + check(cudaGetDeviceCount(&count)); + for (int i = 0; i < count; i++) + { + printf(PRINT_SUFFIX "DEVICE #%d\n", i); + print_device(i); + } + return 0; +} |
