linux nvrtc.nvrtcCompileProgram正在更改locale.getpreferredencoding的输出...如何将其重置为默认值?

uwopmtnx  于 2023-05-06  发布在  Linux
关注(0)|答案(1)|浏览(162)

我在使用cuda-python接口时遇到了一个有趣的副作用。
更详细地说,我发现调用nvrtc.nvrtcCompileProgram会将系统上locale.getpreferredencoding的返回值从UTF-8更改为ANSI_X3.4-1968。这非常烦人,因为它对open函数的默认行为有副作用,在使用其他库时很容易导致UnicodeDecodeError
下面的代码应该重现了这个问题,它是一个非常简单的CUDA程序,它对两个数组A和B求和,并将结果保存在C中。

import numpy as np 
from cuda import cuda, nvrtc
import locale

# interesting interaction between CUDA and OpenCL

# --- define a simple kernel
kernel_code = """
extern "C" __global__ void array_sum(float *A, float *B, float *C, size_t size, size_t n_threads)
{
    size_t tid = blockIdx.x * blockDim.x + threadIdx.x;
    for(size_t i = tid; i<size; i = i + n_threads){
        C[i] = A[i] + B[i];   
    }
}
"""

def cuda_check(err): # fun to control for errors from CUDA/NVRTC
    if isinstance(err, cuda.CUresult):
        if err != cuda.CUresult.CUDA_SUCCESS:
            raise RuntimeError("Cuda Error: {}".format(cuda.cuGetErrorString(err)))
    elif isinstance(err, nvrtc.nvrtcResult):
        if err != nvrtc.nvrtcResult.NVRTC_SUCCESS:
            raise RuntimeError("Nvrtc Error: {}".format(nvrtc.nvrtcGetErrorString(err)))
    else:
        raise RuntimeError("Unknown error type: {}".format(cuda.cuGetErrorString(err)))

### --- initialization --- ###

# --- create 
err, prog = nvrtc.nvrtcCreateProgram(str.encode(kernel_code, encoding='UTF-8'), b"rendering_kernel.cu", 0, [], [])
cuda_check(err)
# --- compile
print(locale.getpreferredencoding(False))      # < ----------- printing preferred locale before
err, = nvrtc.nvrtcCompileProgram(prog, 0, [])
print(locale.getpreferredencoding(False))      # < ----------- and after compiling
cuda_check(err)
err, ptx_size = nvrtc.nvrtcGetPTXSize(prog)
print(f'compilation of NVRTC successful, ptx size {ptx_size}')
ptx = b" " * ptx_size
err, = nvrtc.nvrtcGetPTX(prog, ptx) # PTX is here
cuda_check(err)

# initialize CUDA (single GPU)

# Initialize CUDA Driver API
err, = cuda.cuInit(0)
cuda_check(err)

# Retrieve handle for device 0
err, cuDevice = cuda.cuDeviceGet(0)
cuda_check(err)

# Create context
err, context = cuda.cuCtxCreate(0, cuDevice)   
cuda_check(err) 

# # Load PTX as module data and retrieve function
ptx = np.char.array(ptx)
# Note: Incompatible --gpu-architecture would be detected here
err, module = cuda.cuModuleLoadData(ptx.ctypes.data)
cuda_check(err)
err, kernel = cuda.cuModuleGetFunction(module, b"array_sum")
cuda_check(err)

PROGRAM = prog
NUM_THREADS = 2
NUM_BLOCKS = 2
CONTEXT = context
MODULE = module
KERNEL = kernel

err, stream = cuda.cuStreamCreate(0)
cuda_check(err)

### --- END of initialization --- ###

### --- preparing parameters --- ###

# --- PARAMETERS ---    

def byte_size(x):
    return x.size*x.itemsize

def push_to_stream(np_array, stream):
    err, cls = cuda.cuMemAlloc(byte_size(np_array))
    cuda_check(err)
    err, = cuda.cuMemcpyHtoDAsync(
        cls, np_array.ctypes.data, byte_size(np_array), stream
    )
    cuda_check(err)
    return cls

A = np.array([i for i in range(10)], dtype=np.float32)
B = np.array([9-i for i in range(10)], dtype=np.float32)

print(f"A: {A}")
print(f"B: {B}")

A = push_to_stream(A, stream)
B = push_to_stream(B, stream)
C = push_to_stream(np.zeros([10]).astype(np.float32), stream)

size = np.array(10, dtype=np.uint64)
n_threads = np.array(NUM_BLOCKS*NUM_THREADS, dtype=np.uint64)

def get_ptr(x):
    return np.array(int(x), dtype=np.uint64)

A_ptr = get_ptr(A)
B_ptr = get_ptr(B)
C_ptr = get_ptr(C)

args = [
    A_ptr, B_ptr, C_ptr, size, n_threads
]
# args must be void**
args = np.array([arg.ctypes.data for arg in args], dtype=np.uint64)

err, = cuda.cuLaunchKernel(
    KERNEL,
    NUM_BLOCKS,  # grid x dim
    1,  # grid y dim
    1,  # grid z dim
    NUM_THREADS,  # block x dim
    1,  # block y dim
    1,  # block z dim
    512,  # dynamic shared memory
    stream,  # stream
    args.ctypes.data,  # kernel arguments
    0,  # extra (ignore)
)
cuda_check(err)

### --- DONE preparing parameters --- ###

### --- launching the kernel --- ###

out = np.zeros( [10] ).astype(np.float32)
err, = cuda.cuMemcpyDtoHAsync(
    out.ctypes.data, C, byte_size(out), stream
)
cuda_check(err)

err, = cuda.cuStreamSynchronize(stream)
cuda_check(err)

print(f"C (A+B): {out}")

### --- DONE launching the kernel --- ###

### --- cleaning up --- ###

def cleanup(memory_locs, stream):
    err, = cuda.cuStreamDestroy(stream)
    cuda_check(err)
    for m in memory_locs:
        err, = cuda.cuMemFree(m)
        cuda_check(err)

cleanup([A,B], stream)

err, = nvrtc.nvrtcDestroyProgram(PROGRAM)
cuda_check(err)
err, = cuda.cuModuleUnload(MODULE)
cuda_check(err)
err, = cuda.cuCtxDestroy(CONTEXT)
cuda_check(err)

运行它之后,我得到以下输出:

UTF-8
ANSI_X3.4-1968
compilation of NVRTC successful, ptx size 1343
A: [0. 1. 2. 3. 4. 5. 6. 7. 8. 9.]
B: [9. 8. 7. 6. 5. 4. 3. 2. 1. 0.]
C (A+B): [9. 9. 9. 9. 9. 9. 9. 9. 9. 9.]

前两行显示首选编码的更改情况。
如果我尝试检查哪些是 currentdefault locale,我可以看到它们仍然报告为UTF-8,并且我发现没有简单的方法可以使用locale库中的其他函数更改 preferred locale。
作为参考,请查看以下代码在调用nvrtc之前和之后的输出:

import os 
import locale

print(locale.getdefaultlocale())
locale.resetlocale()
print(locale.getpreferredencoding(False))
print(locale.getlocale(category=locale.LC_CTYPE))
! env | grep LC
locale.localeconv()

之前:

('en_US', 'UTF-8')
UTF-8
('en_US', 'UTF-8')
LC_ALL=C.UTF-8
LC_MEASUREMENT=en_US.UTF-8
LC_PAPER=en_US.UTF-8
LC_MONETARY=en_US.UTF-8
LC_NAME=en_US.UTF-8
LC_COLLATE=en_US.UTF-8
LC_CTYPE=C
LC_ADDRESS=en_US.UTF-8
LC_NUMERIC=en_US.UTF-8
LC_MESSAGES=en_US.UTF-8
LC_TELEPHONE=en_US.UTF-8
LC_IDENTIFICATION=en_US.UTF-8
LC_TIME=en_US.UTF-8
{'int_curr_symbol': 'USD ',
 'currency_symbol': '$',
 'mon_decimal_point': '.',
 'mon_thousands_sep': ',',
 'mon_grouping': [3, 3, 0],
 'positive_sign': '',
 'negative_sign': '-',
 'int_frac_digits': 2,
 'frac_digits': 2,
 'p_cs_precedes': 1,
 'p_sep_by_space': 0,
 'n_cs_precedes': 1,
 'n_sep_by_space': 0,
 'p_sign_posn': 1,
 'n_sign_posn': 1,
 'decimal_point': '.',
 'thousands_sep': ',',
 'grouping': [3, 3, 0]}

编译后:

('en_US', 'UTF-8')
ANSI_X3.4-1968
('en_US', 'UTF-8')
LC_ALL=C.UTF-8
LC_MEASUREMENT=en_US.UTF-8
LC_PAPER=en_US.UTF-8
LC_MONETARY=en_US.UTF-8
LC_NAME=en_US.UTF-8
LC_COLLATE=en_US.UTF-8
LC_CTYPE=C
LC_ADDRESS=en_US.UTF-8
LC_NUMERIC=en_US.UTF-8
LC_MESSAGES=en_US.UTF-8
LC_TELEPHONE=en_US.UTF-8
LC_IDENTIFICATION=en_US.UTF-8
LC_TIME=en_US.UTF-8
{'int_curr_symbol': '',
 'currency_symbol': '',
 'mon_decimal_point': '',
 'mon_thousands_sep': '',
 'mon_grouping': [],
 'positive_sign': '',
 'negative_sign': '',
 'int_frac_digits': 127,
 'frac_digits': 127,
 'p_cs_precedes': 127,
 'p_sep_by_space': 127,
 'n_cs_precedes': 127,
 'n_sep_by_space': 127,
 'p_sign_posn': 127,
 'n_sign_posn': 127,
 'decimal_point': '.',
 'thousands_sep': '',
 'grouping': []}

您可以看到环境仍然设置为UTF-8模式,只是更改了首选编码(及其约定)。
考虑到这一点,我想知道 preferreddefault locale之间的区别是什么,以及是否可以以某种方式更改第一个。
我已经知道locale.getpreferredencoding(False)在后台从_locale库调用_locale.nl_langinfo(_locale.CODESET),这是nl_langinfo(3)函数的直接接口。如果我在相同的环境中从C调用它,我会发现首选的编码仍然是UTF-8,所以我猜更改的影响在某种程度上局限于当前的python示例,在它之外没有任何影响。
无论如何,我想我不是第一个对open函数的默认编码选择有问题的人,幸运的是,它将在python 3.15中被更改。
谢谢大家的帮助,如果需要其他信息,请告诉我。
系统详情:

NAME="Ubuntu"
VERSION="18.04.6 LTS (Bionic Beaver)"

上面的代码在python3.10.6和3.9.12上测试过

xzv2uavs

xzv2uavs1#

这显然是nvrtc中的一个bug。作为解决方法:
只是给予一个小更新。通过与Nvidia讨论这个问题,我们发现可以导出LC_ALL=“POSIX”作为解决方案,以避免NVCC将编码更改为ASCII。
(答案来自评论,并作为社区wiki条目添加,以将问题从CUDA标签的未回答队列中删除)

相关问题