pygpgpu 0.2.0__tar.gz → 0.2.2__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {pygpgpu-0.2.0/pygpgpu.egg-info → pygpgpu-0.2.2}/PKG-INFO +386 -39
- {pygpgpu-0.2.0 → pygpgpu-0.2.2}/README.md +937 -591
- {pygpgpu-0.2.0 → pygpgpu-0.2.2}/README.zh-CN.md +891 -576
- {pygpgpu-0.2.0 → pygpgpu-0.2.2}/examples/01_basic_kernel.py +5 -2
- {pygpgpu-0.2.0 → pygpgpu-0.2.2}/examples/05_gnp_zero_copy_and_atomics.py +115 -111
- pygpgpu-0.2.2/examples/06_handwritten_kernels.py +76 -0
- {pygpgpu-0.2.0 → pygpgpu-0.2.2}/examples/07_low_level_oop.py +66 -66
- {pygpgpu-0.2.0 → pygpgpu-0.2.2}/examples/09_reduction.py +159 -160
- {pygpgpu-0.2.0 → pygpgpu-0.2.2}/examples/11_python_backend.py +83 -84
- pygpgpu-0.2.2/examples/11_returns_and_scope.py +109 -0
- {pygpgpu-0.2.0 → pygpgpu-0.2.2}/examples/13_image.py +1 -1
- pygpgpu-0.2.2/examples/14_heterogeneous_data.py +341 -0
- pygpgpu-0.2.2/examples/15_kernel_import.py +129 -0
- pygpgpu-0.2.2/examples/16_scientific_math.py +63 -0
- pygpgpu-0.2.2/examples/GPU-Puzzles/Puzzle1_Map.py +35 -0
- pygpgpu-0.2.2/examples/GPU-Puzzles/Puzzle2_Zip.py +37 -0
- pygpgpu-0.2.2/examples/GPU-Puzzles/Puzzle3_Guards.py +37 -0
- pygpgpu-0.2.2/examples/GPU-Puzzles/Puzzle4_Map2D.py +37 -0
- pygpgpu-0.2.2/examples/GPU-Puzzles/Puzzle5_Broadcast.py +40 -0
- pygpgpu-0.2.2/examples/GPU-Puzzles/Puzzle6_Blocks.py +37 -0
- pygpgpu-0.2.2/examples/GPU-Puzzles/Puzzle7_Blocks2D.py +38 -0
- pygpgpu-0.2.2/examples/GPU-Puzzles/Puzzle8_Shared.py +48 -0
- pygpgpu-0.2.2/examples/GPU-Puzzles/__cucache__/pykernels/Puzzle1_Map.cu +21 -0
- pygpgpu-0.2.2/examples/GPU-Puzzles/__cucache__/pykernels/Puzzle2_Zip.cu +22 -0
- pygpgpu-0.2.2/examples/GPU-Puzzles/__cucache__/pykernels/Puzzle3_Guards.cu +26 -0
- pygpgpu-0.2.2/examples/GPU-Puzzles/__cucache__/pykernels/Puzzle4_Map2D.cu +29 -0
- pygpgpu-0.2.2/examples/GPU-Puzzles/__cucache__/pykernels/Puzzle5_Broadcast.cu +29 -0
- pygpgpu-0.2.2/examples/GPU-Puzzles/__cucache__/pykernels/Puzzle6_Blocks.cu +26 -0
- pygpgpu-0.2.2/examples/GPU-Puzzles/__cucache__/pykernels/Puzzle7_Blocks2D.cu +29 -0
- pygpgpu-0.2.2/examples/GPU-Puzzles/__cucache__/pykernels/Puzzle8_Shared.cu +34 -0
- {pygpgpu-0.2.0 → pygpgpu-0.2.2}/examples/README.md +82 -75
- {pygpgpu-0.2.0 → pygpgpu-0.2.2}/examples/README.zh-CN.md +66 -59
- pygpgpu-0.2.2/examples/digital_image_processing/01_image_sampling.py +12 -0
- pygpgpu-0.2.2/examples/digital_image_processing/02_image_quantify.py +11 -0
- pygpgpu-0.2.2/examples/digital_image_processing/03_image_brightness_adjustment.py +11 -0
- pygpgpu-0.2.2/examples/digital_image_processing/04_image_contrast_adjustment.py +11 -0
- pygpgpu-0.2.2/examples/digital_image_processing/05_swap_channels.py +10 -0
- pygpgpu-0.2.2/examples/digital_image_processing/06_merge_images.py +12 -0
- pygpgpu-0.2.2/examples/digital_image_processing/07_gamma.py +10 -0
- pygpgpu-0.2.2/examples/digital_image_processing/__clcache__/pykernels/image_processing.cl +45 -0
- pygpgpu-0.2.2/examples/digital_image_processing/__cucache__/pykernels/image_processing.cu +46 -0
- pygpgpu-0.2.2/examples/digital_image_processing/__hipcache__/pykernels/__hipcache__/AMD Radeon(TM) 610M gfx1036/image_processing.hip_5f2fc99ee13d66d4837d4cafdf514803_clean.hip +44 -0
- pygpgpu-0.2.2/examples/digital_image_processing/__hipcache__/pykernels/image_processing.hip +43 -0
- pygpgpu-0.2.2/examples/digital_image_processing/image_processing.py +154 -0
- pygpgpu-0.2.2/examples/kernel_import/README.md +45 -0
- pygpgpu-0.2.2/examples/kernel_import/__hipcache__/AMD Radeon(TM) 610M gfx1036/vecops.hip_2cec05837e5f9def95fd37d804cdd3eb_clean.hip +51 -0
- pygpgpu-0.2.2/examples/kernel_import/__init__.py +17 -0
- pygpgpu-0.2.2/examples/kernel_import/vecops.cl +58 -0
- pygpgpu-0.2.2/examples/kernel_import/vecops.cu +45 -0
- pygpgpu-0.2.2/examples/kernel_import/vecops.hip +47 -0
- pygpgpu-0.2.2/examples/ray_tarcing_in_one_weekend/01_first_image.py +26 -0
- pygpgpu-0.2.2/examples/ray_tarcing_in_one_weekend/camera.py +89 -0
- pygpgpu-0.2.2/examples/ray_tarcing_in_one_weekend/hittable.py +27 -0
- pygpgpu-0.2.2/examples/ray_tarcing_in_one_weekend/interval.py +31 -0
- pygpgpu-0.2.2/examples/ray_tarcing_in_one_weekend/main.py +16 -0
- pygpgpu-0.2.2/examples/ray_tarcing_in_one_weekend/ray.py +11 -0
- pygpgpu-0.2.2/examples/ray_tarcing_in_one_weekend/scene.py +41 -0
- pygpgpu-0.2.2/examples/ray_tarcing_in_one_weekend/sphere.py +44 -0
- pygpgpu-0.2.2/examples/ray_tarcing_in_one_weekend/utils.py +20 -0
- pygpgpu-0.2.2/pygpgpu/__init__.py +190 -0
- {pygpgpu-0.2.0 → pygpgpu-0.2.2}/pygpgpu/cparser/ts_parser.py +155 -33
- pygpgpu-0.2.2/pygpgpu/cuda/__init__.py +10 -0
- {pygpgpu-0.2.0 → pygpgpu-0.2.2}/pygpgpu/cuda/driver/__init__.py +70 -70
- {pygpgpu-0.2.0 → pygpgpu-0.2.2}/pygpgpu/cuda/driver/cuda.py +73 -14
- {pygpgpu-0.2.0 → pygpgpu-0.2.2}/pygpgpu/cuda/driver/cuinfo.py +891 -853
- {pygpgpu-0.2.0 → pygpgpu-0.2.2}/pygpgpu/cuda/driver/cutypes.py +610 -610
- {pygpgpu-0.2.0 → pygpgpu-0.2.2}/pygpgpu/cuda/driver/nvrtc.py +90 -13
- pygpgpu-0.2.2/pygpgpu/cuda/libraries/__init__.py +117 -0
- pygpgpu-0.2.2/pygpgpu/cuda/libraries/_base.py +375 -0
- pygpgpu-0.2.2/pygpgpu/cuda/libraries/cublas.py +425 -0
- pygpgpu-0.2.2/pygpgpu/cuda/libraries/cublaslt.py +356 -0
- pygpgpu-0.2.2/pygpgpu/cuda/libraries/cudart.py +403 -0
- pygpgpu-0.2.2/pygpgpu/cuda/libraries/cufft.py +219 -0
- pygpgpu-0.2.2/pygpgpu/cuda/libraries/cusolver.py +373 -0
- pygpgpu-0.2.2/pygpgpu/cuda/libraries/cusparse.py +343 -0
- pygpgpu-0.2.2/pygpgpu/cuda/libraries/wrappers.py +818 -0
- {pygpgpu-0.2.0 → pygpgpu-0.2.2}/pygpgpu/cuda/oop/buffer.py +26 -14
- {pygpgpu-0.2.0 → pygpgpu-0.2.2}/pygpgpu/cuda/oop/build_options.py +5 -1
- {pygpgpu-0.2.0 → pygpgpu-0.2.2}/pygpgpu/cuda/oop/kernel.py +774 -682
- {pygpgpu-0.2.0 → pygpgpu-0.2.2}/pygpgpu/cuda/oop/nvrtc_program.py +4 -1
- {pygpgpu-0.2.0 → pygpgpu-0.2.2}/pygpgpu/cuda/oop/program_info.py +223 -173
- {pygpgpu-0.2.0 → pygpgpu-0.2.2}/pygpgpu/cuda/oop/program_parser.py +93 -8
- pygpgpu-0.2.2/pygpgpu/hip/__init__.py +9 -0
- {pygpgpu-0.2.0 → pygpgpu-0.2.2}/pygpgpu/hip/driver/__init__.py +28 -28
- pygpgpu-0.2.2/pygpgpu/hip/driver/hip.py +264 -0
- {pygpgpu-0.2.0 → pygpgpu-0.2.2}/pygpgpu/hip/driver/hipinfo.py +6 -0
- {pygpgpu-0.2.0 → pygpgpu-0.2.2}/pygpgpu/hip/oop/buffer.py +26 -14
- {pygpgpu-0.2.0 → pygpgpu-0.2.2}/pygpgpu/hip/oop/hip_compiler.py +238 -209
- {pygpgpu-0.2.0 → pygpgpu-0.2.2}/pygpgpu/hip/oop/kernel.py +88 -6
- {pygpgpu-0.2.0 → pygpgpu-0.2.2}/pygpgpu/hip/oop/program_info.py +58 -5
- {pygpgpu-0.2.0 → pygpgpu-0.2.2}/pygpgpu/hip/oop/program_parser.py +93 -8
- {pygpgpu-0.2.0 → pygpgpu-0.2.2}/pygpgpu/image.py +562 -552
- pygpgpu-0.2.2/pygpgpu/kernel/__init__.py +23 -0
- pygpgpu-0.2.2/pygpgpu/kernel/builtin_registry.py +42 -0
- pygpgpu-0.2.2/pygpgpu/kernel/builtins.py +580 -0
- pygpgpu-0.2.2/pygpgpu/kernel/decorator.py +3263 -0
- pygpgpu-0.2.2/pygpgpu/kernel/device_builtins.py +547 -0
- pygpgpu-0.2.2/pygpgpu/kernel/device_library.py +216 -0
- pygpgpu-0.2.2/pygpgpu/kernel/host_runtime.py +130 -0
- pygpgpu-0.2.2/pygpgpu/kernel/include/quat.h +276 -0
- pygpgpu-0.2.2/pygpgpu/kernel/metal_codegen.py +113 -0
- pygpgpu-0.2.2/pygpgpu/kernel/objects.py +1576 -0
- {pygpgpu-0.2.0 → pygpgpu-0.2.2}/pygpgpu/kernel/python_backend.py +638 -714
- pygpgpu-0.2.2/pygpgpu/kernel/quaternion.py +210 -0
- pygpgpu-0.2.2/pygpgpu/kernel/sci_math.py +608 -0
- pygpgpu-0.2.2/pygpgpu/kernel/source_paths.py +640 -0
- pygpgpu-0.2.2/pygpgpu/kernel/structs.py +644 -0
- pygpgpu-0.2.2/pygpgpu/kernel/translator.py +8494 -0
- pygpgpu-0.2.2/pygpgpu/kernel_import.py +642 -0
- pygpgpu-0.2.2/pygpgpu/kernel_import_stub.py +172 -0
- pygpgpu-0.2.2/pygpgpu/metal/__init__.py +8 -0
- pygpgpu-0.2.2/pygpgpu/metal/driver/__init__.py +2 -0
- pygpgpu-0.2.2/pygpgpu/metal/driver/metal.py +193 -0
- pygpgpu-0.2.2/pygpgpu/metal/driver/objc.py +148 -0
- pygpgpu-0.2.2/pygpgpu/metal/oop/__init__.py +3 -0
- pygpgpu-0.2.2/pygpgpu/metal/oop/buffer.py +196 -0
- pygpgpu-0.2.2/pygpgpu/metal/oop/context.py +99 -0
- pygpgpu-0.2.2/pygpgpu/metal/oop/devices.py +145 -0
- pygpgpu-0.2.2/pygpgpu/metal/oop/event.py +44 -0
- pygpgpu-0.2.2/pygpgpu/metal/oop/kernel.py +308 -0
- pygpgpu-0.2.2/pygpgpu/metal/oop/metalobject.py +69 -0
- pygpgpu-0.2.2/pygpgpu/metal/oop/program.py +123 -0
- pygpgpu-0.2.2/pygpgpu/metal/oop/program_info.py +163 -0
- pygpgpu-0.2.2/pygpgpu/metal/oop/program_parser.py +199 -0
- {pygpgpu-0.2.0 → pygpgpu-0.2.2}/pygpgpu/opencl/__init__.py +7 -6
- {pygpgpu-0.2.0 → pygpgpu-0.2.2}/pygpgpu/opencl/driver/__init__.py +1 -1
- {pygpgpu-0.2.0 → pygpgpu-0.2.2}/pygpgpu/opencl/driver/cl.py +59 -5
- {pygpgpu-0.2.0 → pygpgpu-0.2.2}/pygpgpu/opencl/oop/__init__.py +0 -1
- {pygpgpu-0.2.0 → pygpgpu-0.2.2}/pygpgpu/opencl/oop/buffer.py +29 -13
- {pygpgpu-0.2.0 → pygpgpu-0.2.2}/pygpgpu/opencl/oop/kernel.py +112 -26
- {pygpgpu-0.2.0 → pygpgpu-0.2.2}/pygpgpu/opencl/oop/platforms.py +4 -0
- {pygpgpu-0.2.0 → pygpgpu-0.2.2}/pygpgpu/opencl/oop/program_info.py +32 -2
- {pygpgpu-0.2.0 → pygpgpu-0.2.2}/pygpgpu/opencl/oop/program_parser.py +66 -8
- pygpgpu-0.2.2/pygpgpu/opencl/oop/struct_layout.py +122 -0
- pygpgpu-0.2.2/pygpgpu/py.typed +0 -0
- {pygpgpu-0.2.0 → pygpgpu-0.2.2}/pygpgpu/utils.py +88 -0
- {pygpgpu-0.2.0 → pygpgpu-0.2.2}/pygpgpu/vec_types/__init__.py +66 -51
- pygpgpu-0.2.2/pygpgpu/vec_types/funcs.py +229 -0
- pygpgpu-0.2.2/pygpgpu/vec_types/layout.py +481 -0
- {pygpgpu-0.2.0 → pygpgpu-0.2.2/pygpgpu.egg-info}/PKG-INFO +386 -39
- {pygpgpu-0.2.0 → pygpgpu-0.2.2}/pygpgpu.egg-info/SOURCES.txt +148 -26
- {pygpgpu-0.2.0 → pygpgpu-0.2.2}/pygpgpu.egg-info/requires.txt +3 -3
- {pygpgpu-0.2.0 → pygpgpu-0.2.2}/pyproject.toml +41 -13
- pygpgpu-0.2.2/tests/test_builtins.py +282 -0
- pygpgpu-0.2.2/tests/test_cuda_libraries.py +672 -0
- pygpgpu-0.2.2/tests/test_device_library.py +125 -0
- pygpgpu-0.2.2/tests/test_driver_background_init.py +127 -0
- pygpgpu-0.2.2/tests/test_kernel_aliases_and_ranks.py +345 -0
- pygpgpu-0.2.2/tests/test_kernel_argument_errors.py +338 -0
- pygpgpu-0.2.2/tests/test_kernel_atomic_augassign.py +242 -0
- pygpgpu-0.2.2/tests/test_kernel_atomics.py +202 -0
- pygpgpu-0.2.2/tests/test_kernel_body_arrays.py +151 -0
- pygpgpu-0.2.2/tests/test_kernel_boolean_ops.py +92 -0
- pygpgpu-0.2.2/tests/test_kernel_carried_scalar.py +153 -0
- pygpgpu-0.2.2/tests/test_kernel_clip_where.py +191 -0
- pygpgpu-0.2.2/tests/test_kernel_comparison_builtins.py +200 -0
- pygpgpu-0.2.2/tests/test_kernel_copy_policy.py +81 -0
- pygpgpu-0.2.2/tests/test_kernel_creation.py +367 -0
- pygpgpu-0.2.2/tests/test_kernel_creation_functions.py +296 -0
- pygpgpu-0.2.2/tests/test_kernel_ctypes_structures.py +332 -0
- pygpgpu-0.2.2/tests/test_kernel_ctypes_unions_and_enums.py +320 -0
- pygpgpu-0.2.2/tests/test_kernel_declared_domain.py +146 -0
- pygpgpu-0.2.2/tests/test_kernel_generated_layout.py +174 -0
- pygpgpu-0.2.2/tests/test_kernel_generated_source_cache.py +165 -0
- pygpgpu-0.2.2/tests/test_kernel_generated_spacing.py +155 -0
- pygpgpu-0.2.2/tests/test_kernel_global_array.py +100 -0
- pygpgpu-0.2.2/tests/test_kernel_histogram.py +180 -0
- pygpgpu-0.2.2/tests/test_kernel_host_region_names.py +80 -0
- pygpgpu-0.2.2/tests/test_kernel_host_regions.py +110 -0
- pygpgpu-0.2.2/tests/test_kernel_host_write_roundtrip.py +49 -0
- pygpgpu-0.2.2/tests/test_kernel_import.py +231 -0
- pygpgpu-0.2.2/tests/test_kernel_inplace_and_containment.py +264 -0
- pygpgpu-0.2.2/tests/test_kernel_local_memory.py +306 -0
- pygpgpu-0.2.2/tests/test_kernel_magic_methods.py +198 -0
- pygpgpu-0.2.2/tests/test_kernel_math_builtins.py +140 -0
- pygpgpu-0.2.2/tests/test_kernel_method_calls.py +187 -0
- pygpgpu-0.2.2/tests/test_kernel_narrow_arguments.py +78 -0
- pygpgpu-0.2.2/tests/test_kernel_narrow_types.py +113 -0
- pygpgpu-0.2.2/tests/test_kernel_ndindex_forms.py +141 -0
- pygpgpu-0.2.2/tests/test_kernel_nested_launch.py +275 -0
- pygpgpu-0.2.2/tests/test_kernel_object_arguments.py +529 -0
- pygpgpu-0.2.2/tests/test_kernel_outer_scope.py +366 -0
- pygpgpu-0.2.2/tests/test_kernel_partial_write.py +101 -0
- pygpgpu-0.2.2/tests/test_kernel_quaternion.py +346 -0
- pygpgpu-0.2.2/tests/test_kernel_rank_mismatch.py +125 -0
- pygpgpu-0.2.2/tests/test_kernel_returns.py +389 -0
- pygpgpu-0.2.2/tests/test_kernel_scalar_calls.py +260 -0
- pygpgpu-0.2.2/tests/test_kernel_serial_loop_above_dispatch.py +68 -0
- pygpgpu-0.2.2/tests/test_kernel_shared_scalar.py +198 -0
- pygpgpu-0.2.2/tests/test_kernel_struct_name_hints.py +88 -0
- pygpgpu-0.2.2/tests/test_kernel_struct_types.py +276 -0
- pygpgpu-0.2.2/tests/test_kernel_structure_construction.py +225 -0
- pygpgpu-0.2.2/tests/test_kernel_tail_region.py +62 -0
- pygpgpu-0.2.2/tests/test_kernel_translation_switch.py +141 -0
- pygpgpu-0.2.2/tests/test_kernel_typing.py +204 -0
- pygpgpu-0.2.2/tests/test_kernel_vector3.py +178 -0
- pygpgpu-0.2.2/tests/test_kernel_vector_arguments.py +190 -0
- pygpgpu-0.2.2/tests/test_kernel_vector_layout.py +307 -0
- pygpgpu-0.2.2/tests/test_metal_codegen.py +427 -0
- pygpgpu-0.2.2/tests/test_metal_launch_path.py +302 -0
- pygpgpu-0.2.2/tests/test_sci_math.py +235 -0
- pygpgpu-0.2.2/tests/test_sci_math_hip.py +205 -0
- pygpgpu-0.2.2/tests/test_vector_array_construction.py +114 -0
- pygpgpu-0.2.2/tests/test_work_size_bracket.py +152 -0
- pygpgpu-0.2.0/examples/06_handwritten_kernels.py +0 -60
- pygpgpu-0.2.0/examples/11_texture2d.py +0 -220
- pygpgpu-0.2.0/examples/vecadd_cuda.cu +0 -11
- pygpgpu-0.2.0/pygpgpu/__init__.py +0 -135
- pygpgpu-0.2.0/pygpgpu/cuda/__init__.py +0 -7
- pygpgpu-0.2.0/pygpgpu/cuda/cuimport.py +0 -201
- pygpgpu-0.2.0/pygpgpu/hip/__init__.py +0 -6
- pygpgpu-0.2.0/pygpgpu/hip/driver/hip.py +0 -150
- pygpgpu-0.2.0/pygpgpu/hip/hipimport.py +0 -179
- pygpgpu-0.2.0/pygpgpu/kernel/__init__.py +0 -22
- pygpgpu-0.2.0/pygpgpu/kernel/builtins.py +0 -251
- pygpgpu-0.2.0/pygpgpu/kernel/decorator.py +0 -1025
- pygpgpu-0.2.0/pygpgpu/kernel/translator.py +0 -2963
- pygpgpu-0.2.0/pygpgpu/opencl/climport.py +0 -130
- pygpgpu-0.2.0/pygpgpu/opencl/common.py +0 -76
- pygpgpu-0.2.0/pygpgpu/opencl/oop/kernel_wrapper.py +0 -90
- pygpgpu-0.2.0/pygpgpu/opencl/oop/kernel_wrapper.pyi +0 -45
- pygpgpu-0.2.0/pygpgpu/opencl/oop/program_wrapper.py +0 -109
- pygpgpu-0.2.0/pygpgpu/vec_types/double2.py +0 -14
- pygpgpu-0.2.0/pygpgpu/vec_types/double2.pyi +0 -320
- pygpgpu-0.2.0/pygpgpu/vec_types/double4.py +0 -16
- pygpgpu-0.2.0/pygpgpu/vec_types/double4.pyi +0 -3652
- pygpgpu-0.2.0/pygpgpu/vec_types/float2.py +0 -15
- pygpgpu-0.2.0/pygpgpu/vec_types/float2.pyi +0 -320
- pygpgpu-0.2.0/pygpgpu/vec_types/float4.py +0 -16
- pygpgpu-0.2.0/pygpgpu/vec_types/float4.pyi +0 -3652
- pygpgpu-0.2.0/pygpgpu/vec_types/funcs.py +0 -316
- pygpgpu-0.2.0/pygpgpu/vec_types/int2.py +0 -14
- pygpgpu-0.2.0/pygpgpu/vec_types/int2.pyi +0 -320
- pygpgpu-0.2.0/pygpgpu/vec_types/int4.py +0 -16
- pygpgpu-0.2.0/pygpgpu/vec_types/int4.pyi +0 -3652
- pygpgpu-0.2.0/pygpgpu/vec_types/uint2.py +0 -14
- pygpgpu-0.2.0/pygpgpu/vec_types/uint2.pyi +0 -320
- pygpgpu-0.2.0/pygpgpu/vec_types/uint4.py +0 -16
- pygpgpu-0.2.0/pygpgpu/vec_types/uint4.pyi +0 -3652
- {pygpgpu-0.2.0 → pygpgpu-0.2.2}/LICENSE +0 -0
- {pygpgpu-0.2.0 → pygpgpu-0.2.2}/MANIFEST.in +0 -0
- {pygpgpu-0.2.0 → pygpgpu-0.2.2}/examples/02_backend_device_selection.py +0 -0
- {pygpgpu-0.2.0 → pygpgpu-0.2.2}/examples/03_nd_loop_and_class_method.py +0 -0
- {pygpgpu-0.2.0 → pygpgpu-0.2.2}/examples/04_vector_types.py +0 -0
- {pygpgpu-0.2.0 → pygpgpu-0.2.2}/examples/08_compare_backends.py +0 -0
- {pygpgpu-0.2.0 → pygpgpu-0.2.2}/examples/10_async_launch.py +0 -0
- {pygpgpu-0.2.0 → pygpgpu-0.2.2}/examples/12_print_in_kernel.py +0 -0
- /pygpgpu-0.2.0/pygpgpu/py.typed → /pygpgpu-0.2.2/examples/GPU-Puzzles/Puzzle9_Pooling.py +0 -0
- {pygpgpu-0.2.0 → pygpgpu-0.2.2}/examples/vecadd.cl +0 -0
- {pygpgpu-0.2.0 → pygpgpu-0.2.2}/pygpgpu/constants.py +0 -0
- {pygpgpu-0.2.0 → pygpgpu-0.2.2}/pygpgpu/cparser/ShaderBuiltins.py +0 -0
- {pygpgpu-0.2.0 → pygpgpu-0.2.2}/pygpgpu/cparser/__init__.py +0 -0
- {pygpgpu-0.2.0 → pygpgpu-0.2.2}/pygpgpu/cparser/cparser.py +0 -0
- {pygpgpu-0.2.0 → pygpgpu-0.2.2}/pygpgpu/cparser/cpreprocessor.py +0 -0
- {pygpgpu-0.2.0 → pygpgpu-0.2.2}/pygpgpu/cparser/csyntaxtree.py +0 -0
- {pygpgpu-0.2.0 → pygpgpu-0.2.2}/pygpgpu/cparser/ctokens.py +0 -0
- {pygpgpu-0.2.0 → pygpgpu-0.2.2}/pygpgpu/cparser/generate_code.py +0 -0
- {pygpgpu-0.2.0 → pygpgpu-0.2.2}/pygpgpu/cparser/minifyc.py +0 -0
- {pygpgpu-0.2.0 → pygpgpu-0.2.2}/pygpgpu/cparser/pcpp/__init__.py +0 -0
- {pygpgpu-0.2.0 → pygpgpu-0.2.2}/pygpgpu/cparser/pcpp/evaluator.py +0 -0
- {pygpgpu-0.2.0 → pygpgpu-0.2.2}/pygpgpu/cparser/pcpp/lextab.py +0 -0
- {pygpgpu-0.2.0 → pygpgpu-0.2.2}/pygpgpu/cparser/pcpp/parser.py +0 -0
- {pygpgpu-0.2.0 → pygpgpu-0.2.2}/pygpgpu/cparser/pcpp/parsetab.py +0 -0
- {pygpgpu-0.2.0 → pygpgpu-0.2.2}/pygpgpu/cparser/pcpp/pcmd.py +0 -0
- {pygpgpu-0.2.0 → pygpgpu-0.2.2}/pygpgpu/cparser/pcpp/ply/__init__.py +0 -0
- {pygpgpu-0.2.0 → pygpgpu-0.2.2}/pygpgpu/cparser/pcpp/ply/cpp.py +0 -0
- {pygpgpu-0.2.0 → pygpgpu-0.2.2}/pygpgpu/cparser/pcpp/ply/ctokens.py +0 -0
- {pygpgpu-0.2.0 → pygpgpu-0.2.2}/pygpgpu/cparser/pcpp/ply/lex.py +0 -0
- {pygpgpu-0.2.0 → pygpgpu-0.2.2}/pygpgpu/cparser/pcpp/ply/yacc.py +0 -0
- {pygpgpu-0.2.0 → pygpgpu-0.2.2}/pygpgpu/cparser/pcpp/ply/ygen.py +0 -0
- {pygpgpu-0.2.0 → pygpgpu-0.2.2}/pygpgpu/cparser/pcpp/preprocessor.py +0 -0
- {pygpgpu-0.2.0 → pygpgpu-0.2.2}/pygpgpu/cuda/driver/cuda.pyi +0 -0
- {pygpgpu-0.2.0 → pygpgpu-0.2.2}/pygpgpu/cuda/driver/cudaSurfaceObject_t.py +0 -0
- {pygpgpu-0.2.0 → pygpgpu-0.2.2}/pygpgpu/cuda/driver/cudaTextureObject_t.py +0 -0
- {pygpgpu-0.2.0 → pygpgpu-0.2.2}/pygpgpu/cuda/driver/image_shape.py +0 -0
- {pygpgpu-0.2.0 → pygpgpu-0.2.2}/pygpgpu/cuda/driver/nvrtc.pyi +0 -0
- {pygpgpu-0.2.0 → pygpgpu-0.2.2}/pygpgpu/cuda/driver/nvrtcinfo.py +0 -0
- {pygpgpu-0.2.0 → pygpgpu-0.2.2}/pygpgpu/cuda/driver/nvrtctypes.py +0 -0
- {pygpgpu-0.2.0 → pygpgpu-0.2.2}/pygpgpu/cuda/driver/vec_types/__init__.py +0 -0
- {pygpgpu-0.2.0 → pygpgpu-0.2.2}/pygpgpu/cuda/driver/vec_types/char3.py +0 -0
- {pygpgpu-0.2.0 → pygpgpu-0.2.2}/pygpgpu/cuda/driver/vec_types/char3.pyi +0 -0
- {pygpgpu-0.2.0 → pygpgpu-0.2.2}/pygpgpu/cuda/driver/vec_types/double3.py +0 -0
- {pygpgpu-0.2.0 → pygpgpu-0.2.2}/pygpgpu/cuda/driver/vec_types/double3.pyi +0 -0
- {pygpgpu-0.2.0 → pygpgpu-0.2.2}/pygpgpu/cuda/driver/vec_types/float3.py +0 -0
- {pygpgpu-0.2.0 → pygpgpu-0.2.2}/pygpgpu/cuda/driver/vec_types/float3.pyi +0 -0
- {pygpgpu-0.2.0 → pygpgpu-0.2.2}/pygpgpu/cuda/driver/vec_types/int3.py +0 -0
- {pygpgpu-0.2.0 → pygpgpu-0.2.2}/pygpgpu/cuda/driver/vec_types/int3.pyi +0 -0
- {pygpgpu-0.2.0 → pygpgpu-0.2.2}/pygpgpu/cuda/driver/vec_types/long2.py +0 -0
- {pygpgpu-0.2.0 → pygpgpu-0.2.2}/pygpgpu/cuda/driver/vec_types/long2.pyi +0 -0
- {pygpgpu-0.2.0 → pygpgpu-0.2.2}/pygpgpu/cuda/driver/vec_types/long3.py +0 -0
- {pygpgpu-0.2.0 → pygpgpu-0.2.2}/pygpgpu/cuda/driver/vec_types/long3.pyi +0 -0
- {pygpgpu-0.2.0 → pygpgpu-0.2.2}/pygpgpu/cuda/driver/vec_types/long4.py +0 -0
- {pygpgpu-0.2.0 → pygpgpu-0.2.2}/pygpgpu/cuda/driver/vec_types/long4.pyi +0 -0
- {pygpgpu-0.2.0 → pygpgpu-0.2.2}/pygpgpu/cuda/driver/vec_types/short3.py +0 -0
- {pygpgpu-0.2.0 → pygpgpu-0.2.2}/pygpgpu/cuda/driver/vec_types/short3.pyi +0 -0
- {pygpgpu-0.2.0 → pygpgpu-0.2.2}/pygpgpu/cuda/driver/vec_types/uchar3.py +0 -0
- {pygpgpu-0.2.0 → pygpgpu-0.2.2}/pygpgpu/cuda/driver/vec_types/uchar3.pyi +0 -0
- {pygpgpu-0.2.0 → pygpgpu-0.2.2}/pygpgpu/cuda/driver/vec_types/uint3.py +0 -0
- {pygpgpu-0.2.0 → pygpgpu-0.2.2}/pygpgpu/cuda/driver/vec_types/uint3.pyi +0 -0
- {pygpgpu-0.2.0 → pygpgpu-0.2.2}/pygpgpu/cuda/driver/vec_types/ulong2.py +0 -0
- {pygpgpu-0.2.0 → pygpgpu-0.2.2}/pygpgpu/cuda/driver/vec_types/ulong2.pyi +0 -0
- {pygpgpu-0.2.0 → pygpgpu-0.2.2}/pygpgpu/cuda/driver/vec_types/ulong3.py +0 -0
- {pygpgpu-0.2.0 → pygpgpu-0.2.2}/pygpgpu/cuda/driver/vec_types/ulong3.pyi +0 -0
- {pygpgpu-0.2.0 → pygpgpu-0.2.2}/pygpgpu/cuda/driver/vec_types/ulong4.py +0 -0
- {pygpgpu-0.2.0 → pygpgpu-0.2.2}/pygpgpu/cuda/driver/vec_types/ulong4.pyi +0 -0
- {pygpgpu-0.2.0 → pygpgpu-0.2.2}/pygpgpu/cuda/driver/vec_types/ushort3.py +0 -0
- {pygpgpu-0.2.0 → pygpgpu-0.2.2}/pygpgpu/cuda/driver/vec_types/ushort3.pyi +0 -0
- {pygpgpu-0.2.0 → pygpgpu-0.2.2}/pygpgpu/cuda/oop/__init__.py +0 -0
- {pygpgpu-0.2.0 → pygpgpu-0.2.2}/pygpgpu/cuda/oop/array.py +0 -0
- {pygpgpu-0.2.0 → pygpgpu-0.2.2}/pygpgpu/cuda/oop/context.py +0 -0
- {pygpgpu-0.2.0 → pygpgpu-0.2.2}/pygpgpu/cuda/oop/cuobject.py +0 -0
- {pygpgpu-0.2.0 → pygpgpu-0.2.2}/pygpgpu/cuda/oop/device.py +0 -0
- {pygpgpu-0.2.0 → pygpgpu-0.2.2}/pygpgpu/cuda/oop/device.pyi +0 -0
- {pygpgpu-0.2.0 → pygpgpu-0.2.2}/pygpgpu/cuda/oop/devices.py +0 -0
- {pygpgpu-0.2.0 → pygpgpu-0.2.2}/pygpgpu/cuda/oop/event.py +0 -0
- {pygpgpu-0.2.0 → pygpgpu-0.2.2}/pygpgpu/cuda/oop/mem_object.py +0 -0
- {pygpgpu-0.2.0 → pygpgpu-0.2.2}/pygpgpu/cuda/oop/program.py +0 -0
- {pygpgpu-0.2.0 → pygpgpu-0.2.2}/pygpgpu/cuda/oop/stream.py +0 -0
- {pygpgpu-0.2.0 → pygpgpu-0.2.2}/pygpgpu/cuda/oop/surface.py +0 -0
- {pygpgpu-0.2.0 → pygpgpu-0.2.2}/pygpgpu/cuda/oop/texture.py +0 -0
- {pygpgpu-0.2.0 → pygpgpu-0.2.2}/pygpgpu/exceptions.py +0 -0
- {pygpgpu-0.2.0 → pygpgpu-0.2.2}/pygpgpu/hip/driver/hiptypes.py +0 -0
- {pygpgpu-0.2.0 → pygpgpu-0.2.2}/pygpgpu/hip/oop/__init__.py +0 -0
- {pygpgpu-0.2.0 → pygpgpu-0.2.2}/pygpgpu/hip/oop/build_options.py +0 -0
- {pygpgpu-0.2.0 → pygpgpu-0.2.2}/pygpgpu/hip/oop/context.py +0 -0
- {pygpgpu-0.2.0 → pygpgpu-0.2.2}/pygpgpu/hip/oop/device.py +0 -0
- {pygpgpu-0.2.0 → pygpgpu-0.2.2}/pygpgpu/hip/oop/devices.py +0 -0
- {pygpgpu-0.2.0 → pygpgpu-0.2.2}/pygpgpu/hip/oop/event.py +0 -0
- {pygpgpu-0.2.0 → pygpgpu-0.2.2}/pygpgpu/hip/oop/hipobject.py +0 -0
- {pygpgpu-0.2.0 → pygpgpu-0.2.2}/pygpgpu/hip/oop/mem_object.py +0 -0
- {pygpgpu-0.2.0 → pygpgpu-0.2.2}/pygpgpu/hip/oop/program.py +0 -0
- {pygpgpu-0.2.0 → pygpgpu-0.2.2}/pygpgpu/hip/oop/stream.py +0 -0
- {pygpgpu-0.2.0 → pygpgpu-0.2.2}/pygpgpu/numpy/__init__.py +0 -0
- {pygpgpu-0.2.0 → pygpgpu-0.2.2}/pygpgpu/numpy/array_creation.py +0 -0
- {pygpgpu-0.2.0 → pygpgpu-0.2.2}/pygpgpu/numpy/ndarray.py +0 -0
- {pygpgpu-0.2.0 → pygpgpu-0.2.2}/pygpgpu/opencl/driver/cl.pyi +0 -0
- {pygpgpu-0.2.0 → pygpgpu-0.2.2}/pygpgpu/opencl/driver/clinfo.py +0 -0
- {pygpgpu-0.2.0 → pygpgpu-0.2.2}/pygpgpu/opencl/driver/cltypes.py +0 -0
- {pygpgpu-0.2.0 → pygpgpu-0.2.2}/pygpgpu/opencl/driver/image_types/__init__.py +0 -0
- {pygpgpu-0.2.0 → pygpgpu-0.2.2}/pygpgpu/opencl/driver/image_types/image1d_array_t.py +0 -0
- {pygpgpu-0.2.0 → pygpgpu-0.2.2}/pygpgpu/opencl/driver/image_types/image1d_t.py +0 -0
- {pygpgpu-0.2.0 → pygpgpu-0.2.2}/pygpgpu/opencl/driver/image_types/image2d_array_t.py +0 -0
- {pygpgpu-0.2.0 → pygpgpu-0.2.2}/pygpgpu/opencl/driver/image_types/image2d_t.py +0 -0
- {pygpgpu-0.2.0 → pygpgpu-0.2.2}/pygpgpu/opencl/driver/image_types/image3d_t.py +0 -0
- {pygpgpu-0.2.0 → pygpgpu-0.2.2}/pygpgpu/opencl/driver/image_types/imagend_t.py +0 -0
- {pygpgpu-0.2.0 → pygpgpu-0.2.2}/pygpgpu/opencl/driver/image_types/sampler_t.py +0 -0
- {pygpgpu-0.2.0 → pygpgpu-0.2.2}/pygpgpu/opencl/driver/vec_types/__init__.py +0 -0
- {pygpgpu-0.2.0 → pygpgpu-0.2.2}/pygpgpu/opencl/driver/vec_types/char3.py +0 -0
- {pygpgpu-0.2.0 → pygpgpu-0.2.2}/pygpgpu/opencl/driver/vec_types/char3.pyi +0 -0
- {pygpgpu-0.2.0 → pygpgpu-0.2.2}/pygpgpu/opencl/driver/vec_types/double3.py +0 -0
- {pygpgpu-0.2.0 → pygpgpu-0.2.2}/pygpgpu/opencl/driver/vec_types/double3.pyi +0 -0
- {pygpgpu-0.2.0 → pygpgpu-0.2.2}/pygpgpu/opencl/driver/vec_types/float3.py +0 -0
- {pygpgpu-0.2.0 → pygpgpu-0.2.2}/pygpgpu/opencl/driver/vec_types/float3.pyi +0 -0
- {pygpgpu-0.2.0 → pygpgpu-0.2.2}/pygpgpu/opencl/driver/vec_types/int3.py +0 -0
- {pygpgpu-0.2.0 → pygpgpu-0.2.2}/pygpgpu/opencl/driver/vec_types/int3.pyi +0 -0
- {pygpgpu-0.2.0 → pygpgpu-0.2.2}/pygpgpu/opencl/driver/vec_types/long16.py +0 -0
- {pygpgpu-0.2.0 → pygpgpu-0.2.2}/pygpgpu/opencl/driver/vec_types/long16.pyi +0 -0
- {pygpgpu-0.2.0 → pygpgpu-0.2.2}/pygpgpu/opencl/driver/vec_types/long2.py +0 -0
- {pygpgpu-0.2.0 → pygpgpu-0.2.2}/pygpgpu/opencl/driver/vec_types/long2.pyi +0 -0
- {pygpgpu-0.2.0 → pygpgpu-0.2.2}/pygpgpu/opencl/driver/vec_types/long3.py +0 -0
- {pygpgpu-0.2.0 → pygpgpu-0.2.2}/pygpgpu/opencl/driver/vec_types/long3.pyi +0 -0
- {pygpgpu-0.2.0 → pygpgpu-0.2.2}/pygpgpu/opencl/driver/vec_types/long4.py +0 -0
- {pygpgpu-0.2.0 → pygpgpu-0.2.2}/pygpgpu/opencl/driver/vec_types/long4.pyi +0 -0
- {pygpgpu-0.2.0 → pygpgpu-0.2.2}/pygpgpu/opencl/driver/vec_types/long8.py +0 -0
- {pygpgpu-0.2.0 → pygpgpu-0.2.2}/pygpgpu/opencl/driver/vec_types/long8.pyi +0 -0
- {pygpgpu-0.2.0 → pygpgpu-0.2.2}/pygpgpu/opencl/driver/vec_types/short3.py +0 -0
- {pygpgpu-0.2.0 → pygpgpu-0.2.2}/pygpgpu/opencl/driver/vec_types/short3.pyi +0 -0
- {pygpgpu-0.2.0 → pygpgpu-0.2.2}/pygpgpu/opencl/driver/vec_types/uchar3.py +0 -0
- {pygpgpu-0.2.0 → pygpgpu-0.2.2}/pygpgpu/opencl/driver/vec_types/uchar3.pyi +0 -0
- {pygpgpu-0.2.0 → pygpgpu-0.2.2}/pygpgpu/opencl/driver/vec_types/uint3.py +0 -0
- {pygpgpu-0.2.0 → pygpgpu-0.2.2}/pygpgpu/opencl/driver/vec_types/uint3.pyi +0 -0
- {pygpgpu-0.2.0 → pygpgpu-0.2.2}/pygpgpu/opencl/driver/vec_types/ulong16.py +0 -0
- {pygpgpu-0.2.0 → pygpgpu-0.2.2}/pygpgpu/opencl/driver/vec_types/ulong16.pyi +0 -0
- {pygpgpu-0.2.0 → pygpgpu-0.2.2}/pygpgpu/opencl/driver/vec_types/ulong2.py +0 -0
- {pygpgpu-0.2.0 → pygpgpu-0.2.2}/pygpgpu/opencl/driver/vec_types/ulong2.pyi +0 -0
- {pygpgpu-0.2.0 → pygpgpu-0.2.2}/pygpgpu/opencl/driver/vec_types/ulong3.py +0 -0
- {pygpgpu-0.2.0 → pygpgpu-0.2.2}/pygpgpu/opencl/driver/vec_types/ulong3.pyi +0 -0
- {pygpgpu-0.2.0 → pygpgpu-0.2.2}/pygpgpu/opencl/driver/vec_types/ulong4.py +0 -0
- {pygpgpu-0.2.0 → pygpgpu-0.2.2}/pygpgpu/opencl/driver/vec_types/ulong4.pyi +0 -0
- {pygpgpu-0.2.0 → pygpgpu-0.2.2}/pygpgpu/opencl/driver/vec_types/ulong8.py +0 -0
- {pygpgpu-0.2.0 → pygpgpu-0.2.2}/pygpgpu/opencl/driver/vec_types/ulong8.pyi +0 -0
- {pygpgpu-0.2.0 → pygpgpu-0.2.2}/pygpgpu/opencl/driver/vec_types/ushort3.py +0 -0
- {pygpgpu-0.2.0 → pygpgpu-0.2.2}/pygpgpu/opencl/driver/vec_types/ushort3.pyi +0 -0
- {pygpgpu-0.2.0 → pygpgpu-0.2.2}/pygpgpu/opencl/oop/build_options.py +0 -0
- {pygpgpu-0.2.0 → pygpgpu-0.2.2}/pygpgpu/opencl/oop/clobject.py +0 -0
- {pygpgpu-0.2.0 → pygpgpu-0.2.2}/pygpgpu/opencl/oop/command_queue.py +0 -0
- {pygpgpu-0.2.0 → pygpgpu-0.2.2}/pygpgpu/opencl/oop/command_queue.pyi +0 -0
- {pygpgpu-0.2.0 → pygpgpu-0.2.2}/pygpgpu/opencl/oop/context.py +0 -0
- {pygpgpu-0.2.0 → pygpgpu-0.2.2}/pygpgpu/opencl/oop/context.pyi +0 -0
- {pygpgpu-0.2.0 → pygpgpu-0.2.2}/pygpgpu/opencl/oop/device.py +0 -0
- {pygpgpu-0.2.0 → pygpgpu-0.2.2}/pygpgpu/opencl/oop/device.pyi +0 -0
- {pygpgpu-0.2.0 → pygpgpu-0.2.2}/pygpgpu/opencl/oop/event.py +0 -0
- {pygpgpu-0.2.0 → pygpgpu-0.2.2}/pygpgpu/opencl/oop/event.pyi +0 -0
- {pygpgpu-0.2.0 → pygpgpu-0.2.2}/pygpgpu/opencl/oop/image1d.py +0 -0
- {pygpgpu-0.2.0 → pygpgpu-0.2.2}/pygpgpu/opencl/oop/image1d_array.py +0 -0
- {pygpgpu-0.2.0 → pygpgpu-0.2.2}/pygpgpu/opencl/oop/image2d.py +0 -0
- {pygpgpu-0.2.0 → pygpgpu-0.2.2}/pygpgpu/opencl/oop/image2d_array.py +0 -0
- {pygpgpu-0.2.0 → pygpgpu-0.2.2}/pygpgpu/opencl/oop/image3d.py +0 -0
- {pygpgpu-0.2.0 → pygpgpu-0.2.2}/pygpgpu/opencl/oop/imagend.py +0 -0
- {pygpgpu-0.2.0 → pygpgpu-0.2.2}/pygpgpu/opencl/oop/kernel.pyi +0 -0
- {pygpgpu-0.2.0 → pygpgpu-0.2.2}/pygpgpu/opencl/oop/mem_object.py +0 -0
- {pygpgpu-0.2.0 → pygpgpu-0.2.2}/pygpgpu/opencl/oop/mem_object.pyi +0 -0
- {pygpgpu-0.2.0 → pygpgpu-0.2.2}/pygpgpu/opencl/oop/pipe.py +0 -0
- {pygpgpu-0.2.0 → pygpgpu-0.2.2}/pygpgpu/opencl/oop/platform.py +0 -0
- {pygpgpu-0.2.0 → pygpgpu-0.2.2}/pygpgpu/opencl/oop/platform.pyi +0 -0
- {pygpgpu-0.2.0 → pygpgpu-0.2.2}/pygpgpu/opencl/oop/program.py +0 -0
- {pygpgpu-0.2.0 → pygpgpu-0.2.2}/pygpgpu/opencl/oop/program.pyi +0 -0
- {pygpgpu-0.2.0 → pygpgpu-0.2.2}/pygpgpu/opencl/oop/sampler.py +0 -0
- {pygpgpu-0.2.0 → pygpgpu-0.2.2}/pygpgpu/opencl/oop/sampler.pyi +0 -0
- {pygpgpu-0.2.0 → pygpgpu-0.2.2}/pygpgpu/opencl/oop/svm_buffer.py +0 -0
- {pygpgpu-0.2.0 → pygpgpu-0.2.2}/pygpgpu/vec_types/char16.py +0 -0
- {pygpgpu-0.2.0 → pygpgpu-0.2.2}/pygpgpu/vec_types/char16.pyi +0 -0
- {pygpgpu-0.2.0 → pygpgpu-0.2.2}/pygpgpu/vec_types/char2.py +0 -0
- {pygpgpu-0.2.0 → pygpgpu-0.2.2}/pygpgpu/vec_types/char2.pyi +0 -0
- {pygpgpu-0.2.0 → pygpgpu-0.2.2}/pygpgpu/vec_types/char4.py +0 -0
- {pygpgpu-0.2.0 → pygpgpu-0.2.2}/pygpgpu/vec_types/char4.pyi +0 -0
- {pygpgpu-0.2.0 → pygpgpu-0.2.2}/pygpgpu/vec_types/char8.py +0 -0
- {pygpgpu-0.2.0 → pygpgpu-0.2.2}/pygpgpu/vec_types/char8.pyi +0 -0
- {pygpgpu-0.2.0 → pygpgpu-0.2.2}/pygpgpu/vec_types/double16.py +0 -0
- {pygpgpu-0.2.0 → pygpgpu-0.2.2}/pygpgpu/vec_types/double16.pyi +0 -0
- {pygpgpu-0.2.0 → pygpgpu-0.2.2}/pygpgpu/vec_types/double8.py +0 -0
- {pygpgpu-0.2.0 → pygpgpu-0.2.2}/pygpgpu/vec_types/double8.pyi +0 -0
- {pygpgpu-0.2.0 → pygpgpu-0.2.2}/pygpgpu/vec_types/float16.py +0 -0
- {pygpgpu-0.2.0 → pygpgpu-0.2.2}/pygpgpu/vec_types/float16.pyi +0 -0
- {pygpgpu-0.2.0 → pygpgpu-0.2.2}/pygpgpu/vec_types/float8.py +0 -0
- {pygpgpu-0.2.0 → pygpgpu-0.2.2}/pygpgpu/vec_types/float8.pyi +0 -0
- {pygpgpu-0.2.0 → pygpgpu-0.2.2}/pygpgpu/vec_types/genVec.py +0 -0
- {pygpgpu-0.2.0 → pygpgpu-0.2.2}/pygpgpu/vec_types/genVec16.py +0 -0
- {pygpgpu-0.2.0 → pygpgpu-0.2.2}/pygpgpu/vec_types/genVec16.pyi.in +0 -0
- {pygpgpu-0.2.0 → pygpgpu-0.2.2}/pygpgpu/vec_types/genVec2.py +0 -0
- {pygpgpu-0.2.0 → pygpgpu-0.2.2}/pygpgpu/vec_types/genVec2.pyi.in +0 -0
- {pygpgpu-0.2.0 → pygpgpu-0.2.2}/pygpgpu/vec_types/genVec3.py +0 -0
- {pygpgpu-0.2.0 → pygpgpu-0.2.2}/pygpgpu/vec_types/genVec3.pyi.in +0 -0
- {pygpgpu-0.2.0 → pygpgpu-0.2.2}/pygpgpu/vec_types/genVec4.py +0 -0
- {pygpgpu-0.2.0 → pygpgpu-0.2.2}/pygpgpu/vec_types/genVec4.pyi.in +0 -0
- {pygpgpu-0.2.0 → pygpgpu-0.2.2}/pygpgpu/vec_types/genVec8.py +0 -0
- {pygpgpu-0.2.0 → pygpgpu-0.2.2}/pygpgpu/vec_types/genVec8.pyi.in +0 -0
- {pygpgpu-0.2.0 → pygpgpu-0.2.2}/pygpgpu/vec_types/helper.py +0 -0
- {pygpgpu-0.2.0 → pygpgpu-0.2.2}/pygpgpu/vec_types/int16.py +0 -0
- {pygpgpu-0.2.0 → pygpgpu-0.2.2}/pygpgpu/vec_types/int16.pyi +0 -0
- {pygpgpu-0.2.0 → pygpgpu-0.2.2}/pygpgpu/vec_types/int8.py +0 -0
- {pygpgpu-0.2.0 → pygpgpu-0.2.2}/pygpgpu/vec_types/int8.pyi +0 -0
- {pygpgpu-0.2.0 → pygpgpu-0.2.2}/pygpgpu/vec_types/short16.py +0 -0
- {pygpgpu-0.2.0 → pygpgpu-0.2.2}/pygpgpu/vec_types/short16.pyi +0 -0
- {pygpgpu-0.2.0 → pygpgpu-0.2.2}/pygpgpu/vec_types/short2.py +0 -0
- {pygpgpu-0.2.0 → pygpgpu-0.2.2}/pygpgpu/vec_types/short2.pyi +0 -0
- {pygpgpu-0.2.0 → pygpgpu-0.2.2}/pygpgpu/vec_types/short4.py +0 -0
- {pygpgpu-0.2.0 → pygpgpu-0.2.2}/pygpgpu/vec_types/short4.pyi +0 -0
- {pygpgpu-0.2.0 → pygpgpu-0.2.2}/pygpgpu/vec_types/short8.py +0 -0
- {pygpgpu-0.2.0 → pygpgpu-0.2.2}/pygpgpu/vec_types/short8.pyi +0 -0
- {pygpgpu-0.2.0 → pygpgpu-0.2.2}/pygpgpu/vec_types/uchar16.py +0 -0
- {pygpgpu-0.2.0 → pygpgpu-0.2.2}/pygpgpu/vec_types/uchar16.pyi +0 -0
- {pygpgpu-0.2.0 → pygpgpu-0.2.2}/pygpgpu/vec_types/uchar2.py +0 -0
- {pygpgpu-0.2.0 → pygpgpu-0.2.2}/pygpgpu/vec_types/uchar2.pyi +0 -0
- {pygpgpu-0.2.0 → pygpgpu-0.2.2}/pygpgpu/vec_types/uchar4.py +0 -0
- {pygpgpu-0.2.0 → pygpgpu-0.2.2}/pygpgpu/vec_types/uchar4.pyi +0 -0
- {pygpgpu-0.2.0 → pygpgpu-0.2.2}/pygpgpu/vec_types/uchar8.py +0 -0
- {pygpgpu-0.2.0 → pygpgpu-0.2.2}/pygpgpu/vec_types/uchar8.pyi +0 -0
- {pygpgpu-0.2.0 → pygpgpu-0.2.2}/pygpgpu/vec_types/uint16.py +0 -0
- {pygpgpu-0.2.0 → pygpgpu-0.2.2}/pygpgpu/vec_types/uint16.pyi +0 -0
- {pygpgpu-0.2.0 → pygpgpu-0.2.2}/pygpgpu/vec_types/uint8.py +0 -0
- {pygpgpu-0.2.0 → pygpgpu-0.2.2}/pygpgpu/vec_types/uint8.pyi +0 -0
- {pygpgpu-0.2.0 → pygpgpu-0.2.2}/pygpgpu/vec_types/ushort16.py +0 -0
- {pygpgpu-0.2.0 → pygpgpu-0.2.2}/pygpgpu/vec_types/ushort16.pyi +0 -0
- {pygpgpu-0.2.0 → pygpgpu-0.2.2}/pygpgpu/vec_types/ushort2.py +0 -0
- {pygpgpu-0.2.0 → pygpgpu-0.2.2}/pygpgpu/vec_types/ushort2.pyi +0 -0
- {pygpgpu-0.2.0 → pygpgpu-0.2.2}/pygpgpu/vec_types/ushort4.py +0 -0
- {pygpgpu-0.2.0 → pygpgpu-0.2.2}/pygpgpu/vec_types/ushort4.pyi +0 -0
- {pygpgpu-0.2.0 → pygpgpu-0.2.2}/pygpgpu/vec_types/ushort8.py +0 -0
- {pygpgpu-0.2.0 → pygpgpu-0.2.2}/pygpgpu/vec_types/ushort8.pyi +0 -0
- {pygpgpu-0.2.0 → pygpgpu-0.2.2}/pygpgpu.egg-info/dependency_links.txt +0 -0
- {pygpgpu-0.2.0 → pygpgpu-0.2.2}/pygpgpu.egg-info/top_level.txt +0 -0
- {pygpgpu-0.2.0 → pygpgpu-0.2.2}/setup.cfg +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: pygpgpu
|
|
3
|
-
Version: 0.2.
|
|
3
|
+
Version: 0.2.2
|
|
4
4
|
Summary: Run Python functions as GPU kernels with the @kernel decorator (OpenCL / CUDA / HIP); handwritten .cl/.cu/.hip kernels import like Python modules
|
|
5
5
|
Author-email: "王炳辉 (Bing-Hui WANG)" <binghui.wang@foxmail.com>
|
|
6
6
|
License: MIT License
|
|
@@ -32,6 +32,7 @@ Classifier: Intended Audience :: Developers
|
|
|
32
32
|
Classifier: Intended Audience :: Science/Research
|
|
33
33
|
Classifier: License :: OSI Approved :: MIT License
|
|
34
34
|
Classifier: Operating System :: Microsoft :: Windows
|
|
35
|
+
Classifier: Operating System :: POSIX :: Linux
|
|
35
36
|
Classifier: Programming Language :: Python :: 3
|
|
36
37
|
Classifier: Programming Language :: Python :: 3.7
|
|
37
38
|
Classifier: Programming Language :: Python :: 3.8
|
|
@@ -50,9 +51,9 @@ License-File: LICENSE
|
|
|
50
51
|
Requires-Dist: numpy>=1.21
|
|
51
52
|
Requires-Dist: imageio>=2.30
|
|
52
53
|
Requires-Dist: tree-sitter>=0.20.0
|
|
53
|
-
Requires-Dist: tree-sitter-opencl>=0.0.
|
|
54
|
-
Requires-Dist: tree-sitter-cuda37>=0.21.
|
|
55
|
-
Requires-Dist: pygf>=0.
|
|
54
|
+
Requires-Dist: tree-sitter-opencl>=0.0.3
|
|
55
|
+
Requires-Dist: tree-sitter-cuda37>=0.21.3
|
|
56
|
+
Requires-Dist: pygf>=0.2.0
|
|
56
57
|
Requires-Dist: typing_extensions>=4.4.0
|
|
57
58
|
Provides-Extra: dev
|
|
58
59
|
Requires-Dist: pytest>=8; extra == "dev"
|
|
@@ -64,7 +65,7 @@ Dynamic: license-file
|
|
|
64
65
|
|
|
65
66
|
**English** | [Chinese](README.zh-CN.md)
|
|
66
67
|
|
|
67
|
-
Decorate a Python function and it runs on the GPU — one codebase,
|
|
68
|
+
Decorate a Python function and it runs on the GPU — one codebase, four backends (**OpenCL / CUDA / HIP / Metal**).
|
|
68
69
|
|
|
69
70
|
```python
|
|
70
71
|
import numpy as np
|
|
@@ -78,7 +79,7 @@ def saxpy(x: np.ndarray, y: np.ndarray, out: np.ndarray, alpha: float) -> None:
|
|
|
78
79
|
x = np.arange(1_000_000, dtype=np.float32)
|
|
79
80
|
out = np.zeros_like(x)
|
|
80
81
|
|
|
81
|
-
saxpy(x, x, out, 2.0) # no backend given: probe cuda -> hip -> opencl, take the first available
|
|
82
|
+
saxpy(x, x, out, 2.0) # no backend given: probe cuda -> hip -> metal -> opencl, take the first available
|
|
82
83
|
saxpy["cuda"](x, x, out, 2.0) # bracket switches the backend
|
|
83
84
|
saxpy["hip:gfx1036"](x, x, out, 2.0) # backend:device
|
|
84
85
|
saxpy["python"](x, x, out, 2.0) # the same function, serially in the interpreter (no GPU)
|
|
@@ -90,25 +91,26 @@ No C code to write, no build system to configure; the first call translates, com
|
|
|
90
91
|
|
|
91
92
|
- **`@kernel` decorator** — decorate a Python function directly (class methods and static methods included); at call time it is specialized per argument type, translated to backend source and compiled (monomorphization).
|
|
92
93
|
- **Plain-Python helper functions** — a helper called from a kernel needs no decorator, and its annotations are optional: it is translated into a device function and monomorphized from the call site, recursively for helpers that call helpers. The same holds for helpers reached through a name: `module.func(...)`, `SomeClass.static_method(...)`, `self.static_method(...)`, and `self.other_method(...)` between methods of one class.
|
|
93
|
-
- **
|
|
94
|
+
- **Every backend in one process** — the bracket chooses the backend per call, so a single process (and even a single kernel object) can mix OpenCL, CUDA, HIP and Metal; each backend/device pair is specialized and cached separately.
|
|
94
95
|
- **Pure-Python backend** — `k["python"]` runs the same `@kernel` body in the interpreter, serially on the CPU: no GPU, no compiler, no runtime. Useful for testing, for small inputs and as a reference for what the GPU semantics mean.
|
|
95
|
-
- **Handwritten kernel file import** — `.cl` / `.cu` / `.hip` files import like Python modules: kernels and struct types become module attributes, and a `.pyi` type stub is generated automatically.
|
|
96
|
+
- **Handwritten kernel file import** — `.cl` / `.cu` / `.hip` / `.metal` files import like Python modules: kernels and struct types become module attributes, and a `.pyi` type stub is generated automatically.
|
|
96
97
|
- **Low-level OOP API** — Device/Context/Buffer/Program/Kernel, for when you need full control.
|
|
97
98
|
- **One bracket syntax** for backend/device/work size (identical to handwritten kernels, order does not matter):
|
|
98
99
|
```python
|
|
99
100
|
k(args) # fully automatic
|
|
100
|
-
k["cuda"](args) # backend (opencl/cuda/hip/python, aliases ocl/cu/rocm/amd/py)
|
|
101
|
+
k["cuda"](args) # backend (opencl/cuda/hip/metal/python, aliases ocl/cu/rocm/amd/mtl/py)
|
|
101
102
|
k["opencl:gfx1036"](args) # backend:device
|
|
102
|
-
k[(
|
|
103
|
-
k["cuda"][(
|
|
103
|
+
k[(4, 256)](args) # work size: (blocks per grid, threads per block)
|
|
104
|
+
k["cuda"][(4, 256)](args) # combined
|
|
104
105
|
```
|
|
105
|
-
- **
|
|
106
|
+
- **CUDA work-size brackets on every backend**: (blocks_per_grid, threads_per_block), the spelling a CUDA or HIP reader already writes. k[(4, 256)] launches 4 blocks of 256 threads (1024 work items); k[(n,)] is a bare item count with an automatic block size, and None means automatic. The same bracket works on every backend, in @kernel and in the low-level oop API, and a block count never has to divide anything: the total is locks * threads by construction.
|
|
106
107
|
- **Static read/write analysis**: the kernel body is parsed to infer whether each pointer argument is read-only, write-only or read-write — write-only arguments skip the upload, read-only arguments skip the read-back, and data stays on the device across calls (zero intermediate copies for gnp arrays).
|
|
107
108
|
- **Vector types**: 50 vector types (float4/int3/...) with `.x/.y/.z/.w` members, swizzles, construction, arithmetic (scalar broadcast), comparisons and component-wise math.
|
|
108
109
|
- **Dual-sided function library** `pygpgpu.funcs` (aka `pygpgpu.vec_types.funcs`): `dot/cross/normalize/clamp/...` run as-is in host Python and are translated to device builtins inside `@kernel`.
|
|
109
|
-
- **Device
|
|
110
|
+
- **Device builtins, exported at the top level**: `get_global_id`, `barrier`, `atomic_add`, `dot`, ... are plain functions imported from `pygpgpu`. Inside a `@kernel` body the translator recognizes each one by the object it resolves to and maps it to the call of every backend, so the same kernel body runs on OpenCL, CUDA, HIP and in the interpreter.
|
|
110
111
|
- **Multi-dimensional iteration**: `np.ndindex(shape)` / `np.ndenumerate(arr)` magic loops (2D/3D), `enumerate(arr)` for a 1D index plus value snapshot.
|
|
111
112
|
- **Three cache layers**: parsed metadata / compiled artifacts / generated sources, all invalidated by mtime — unchanged sources mean zero recompilation.
|
|
113
|
+
- **Host-side CUDA libraries bound with ctypes** — cuBLAS, cuBLASLt, cuSOLVER, cuFFT, cuSPARSE and the CUDA runtime, in the same shape as the driver (lazy DLL, signature table, the library's own error messages), with a numpy-friendly layer over them (`gemm`, `axpy`, `scal`, `dot`, `rfft`, `csrmv`). See "Host-side CUDA libraries" below.
|
|
112
114
|
|
|
113
115
|
## Installation
|
|
114
116
|
|
|
@@ -122,31 +124,32 @@ Python ≥ 3.7. Bring your own GPU runtime (any one of them is enough):
|
|
|
122
124
|
|---|---|---|
|
|
123
125
|
| OpenCL | GPU driver (usually already installed) | Nothing extra to install; works across NVIDIA/AMD/Intel |
|
|
124
126
|
| CUDA | NVIDIA driver (includes NVRTC) | The Toolkit is not required — the driver-provided nvrtc is enough |
|
|
125
|
-
| HIP | AMD HIP SDK |
|
|
127
|
+
| HIP | AMD HIP SDK | Compilation runs through the SDK's clang: on Windows that is `<ROCm>/bin/clang.exe` with the MSVC/Windows SDK headers auto-discovered (point `PYGPGPU_HIP_INCLUDES` at the include directories, os.pathsep-separated, if they are not on the default paths); on Linux it is `<ROCm>/bin/clang` (or `hipcc` on `PATH`), which finds the system headers itself |
|
|
128
|
+
| Metal | macOS on Apple hardware | The Metal backend compiles through the framework's own runtime compiler, so no Xcode command-line tools are needed. It requires a real Apple GPU: a Mac virtual machine has no Metal device, so this backend is unavailable there |
|
|
126
129
|
| Python | nothing | Runs the `@kernel` body serially in the interpreter; selected explicitly with `k["python"]` and never probed for |
|
|
127
130
|
|
|
128
|
-
When `@kernel` has no explicit `backend`, backends are probed in **cuda -> hip -> opencl** order and the first one that enumerates a device wins.
|
|
131
|
+
When `@kernel` has no explicit `backend`, backends are probed in **cuda -> hip -> metal -> opencl** order and the first one that enumerates a device wins. The order puts Metal ahead of OpenCL on macOS, where OpenCL is deprecated and only exposes CPU devices.
|
|
129
132
|
|
|
130
133
|
## Quick start
|
|
131
134
|
|
|
132
135
|
Every commonly used API is importable straight from the `pygpgpu` top level — no need to remember submodule names:
|
|
133
136
|
|
|
134
137
|
```python
|
|
135
|
-
from pygpgpu import kernel,
|
|
138
|
+
from pygpgpu import kernel, funcs, float4 # @kernel / dual-sided funcs / vector types
|
|
136
139
|
from pygpgpu import list_devices, get_device
|
|
137
|
-
from pygpgpu import
|
|
140
|
+
from pygpgpu import kernel_import # the import hook for handwritten .cl/.cu/.hip files
|
|
138
141
|
```
|
|
139
142
|
|
|
140
143
|
The submodule paths (`pygpgpu.kernel` / `pygpgpu.vec_types` / `pygpgpu.opencl` / ...) still work. Note that top-level `pygpgpu.kernel` is the `@kernel` decorator; to reach things inside the submodule write `from pygpgpu.kernel import ...`.
|
|
141
144
|
|
|
142
|
-
|
|
145
|
+
`kernel_import` needs no GPU runtime to be imported or installed, and a merged kernel loads only its own backend's runtime when it is first called.
|
|
143
146
|
|
|
144
147
|
### The @kernel decorator
|
|
145
148
|
|
|
146
149
|
```python
|
|
147
150
|
import math
|
|
148
151
|
import numpy as np
|
|
149
|
-
from pygpgpu import kernel,
|
|
152
|
+
from pygpgpu import kernel, funcs, float4
|
|
150
153
|
|
|
151
154
|
def norm2(v: float4) -> float:
|
|
152
155
|
return funcs.dot(v, v)
|
|
@@ -161,18 +164,69 @@ def matrix_scale(m: np.ndarray, out: np.ndarray, k: float) -> None:
|
|
|
161
164
|
for y, x in np.ndindex(out.shape): # 2D NDRange
|
|
162
165
|
out[y, x] = m[y, x] * k
|
|
163
166
|
|
|
164
|
-
@kernel(backend="cuda")
|
|
167
|
+
@kernel(backend="cuda", domain="data")
|
|
165
168
|
def histogram(data: np.ndarray, counts: np.ndarray) -> None:
|
|
166
|
-
|
|
167
|
-
i
|
|
168
|
-
gpu.atomic_add(counts, data[i], 1) # atomics, results read back automatically
|
|
169
|
+
i = get_global_id(0)
|
|
170
|
+
atomic_add(counts, data[i], 1) # atomics, results read back automatically
|
|
169
171
|
```
|
|
170
172
|
|
|
171
173
|
Supported: scalar/array/vector arguments, if/while/for-range, arithmetic/comparison/bit ops, `math.*` and `np.*` math functions, class methods (`self.xxx` attributes become arguments automatically), static methods, and calling helpers through a name — a module-level function, `module.func(...)`, `SomeClass.static_method(...)`, and `self.method(...)` for another method of the same class. Unsupported syntax raises a `SyntaxError` carrying the line number.
|
|
172
174
|
|
|
175
|
+
### `+=` into an array element
|
|
176
|
+
|
|
177
|
+
`arr[i] op= value` follows Python's semantics even when several work items meet at the same element: `bins[image[i, j, k]] += 1` accumulates every pixel, without writing `atomic_add` by hand. The translator emits a device atomic whenever it cannot prove the element belongs to one work item — the proof being that the subscript is built from exactly the magic loop's own index variables, so `out[i] += x[i]` stays a plain (faster) read-modify-write.
|
|
178
|
+
|
|
179
|
+
```python
|
|
180
|
+
@kernel
|
|
181
|
+
def histogram(image: np.ndarray):
|
|
182
|
+
bins: np.ndarray = np.zeros((3, 256), dtype=np.int32) # created on the host
|
|
183
|
+
for i, j, k in np.ndindex(image.shape):
|
|
184
|
+
bins[k, image[i, j, k]] += 1 # one atomic add per pixel: no lost counts
|
|
185
|
+
return bins
|
|
186
|
+
```
|
|
187
|
+
|
|
188
|
+
Only operators with a device atomic counterpart are automated (`+=`, `-=`, `&=`, `|=`, `^=`). A racy `*=`/`/=`/`**=`, or a racy accumulation into a float element, raises a `SyntaxError` instead of losing data: there is no portable float atomic on the OpenCL backend, so use an integer buffer or accumulate per work item and reduce afterwards (`examples/09_reduction.py`).
|
|
189
|
+
|
|
190
|
+
### Accumulating into a single scalar
|
|
191
|
+
|
|
192
|
+
A handwritten kernel takes a pointer to one element (`__global int* total`) and passes it to `atomicAdd`. There are three equivalent shapes here:
|
|
193
|
+
|
|
194
|
+
```python
|
|
195
|
+
from pygpgpu import atomic_add, kernel
|
|
196
|
+
|
|
197
|
+
# 1) a 1-element array IS that pointer
|
|
198
|
+
@kernel
|
|
199
|
+
def count(x: np.ndarray, total: np.ndarray):
|
|
200
|
+
for i in range(x.shape[0]):
|
|
201
|
+
if x[i] > 0.0:
|
|
202
|
+
atomic_add(total, 0, 1) # or simply: total[0] += 1
|
|
203
|
+
|
|
204
|
+
# 2) an outer scalar declared nonlocal/global becomes a device pointer,
|
|
205
|
+
# and the result is written back to the enclosing scope after the call
|
|
206
|
+
@kernel
|
|
207
|
+
def count_global(x: np.ndarray):
|
|
208
|
+
global TOTAL
|
|
209
|
+
for i in range(x.shape[0]):
|
|
210
|
+
if x[i] > 0.0:
|
|
211
|
+
TOTAL += 1 # atomic on the device, native in the interpreter
|
|
212
|
+
|
|
213
|
+
# 3) a counter created in the body can be returned as the result
|
|
214
|
+
@kernel
|
|
215
|
+
def count_and_return(x: np.ndarray):
|
|
216
|
+
total: np.ndarray = np.zeros(1, dtype=np.int32)
|
|
217
|
+
for i in range(x.shape[0]):
|
|
218
|
+
if x[i] > 0.0:
|
|
219
|
+
total[0] += 1
|
|
220
|
+
return total # a 1-element array: the caller reads [0]
|
|
221
|
+
```
|
|
222
|
+
|
|
223
|
+
All three are exact on every backend — thousands of work items incrementing one location lose nothing. An outer scalar is always a *shared* location, so `TOTAL += 1` there is always atomic; `atomic_add(scalar, 0, 1)` is refused with a message pointing at that spelling, since an atomic needs an array to index.
|
|
224
|
+
|
|
225
|
+
|
|
226
|
+
|
|
173
227
|
Type annotations are optional: the snippets in this README carry them for readability, but the device types come from the arguments at call time, so the same code works unannotated (`self` is not a kernel argument, so annotating it changes nothing).
|
|
174
228
|
|
|
175
|
-
A kernel needs an iteration domain for its work size to be derived. Either use a magic loop (`for i in range(x.shape[0])`, whose bound is the domain) or, when dispatching on `
|
|
229
|
+
A kernel needs an iteration domain for its work size to be derived. Either use a magic loop (`for i in range(x.shape[0])`, whose bound is the domain) or, when dispatching on `get_global_id`, name the array in the decorator: `@kernel(domain="x")`. The domain contributes all of `x`'s dimensions, and a `k[(blocks, threads)]` bracket also works. Without any of these the call raises instead of guessing.
|
|
176
230
|
|
|
177
231
|
### Printing from a kernel
|
|
178
232
|
|
|
@@ -245,14 +299,30 @@ The two kernel styles keep their meanings, which is where the interpreter differ
|
|
|
245
299
|
|
|
246
300
|
| Kernel style | What `k["python"](...)` does |
|
|
247
301
|
|---|---|
|
|
248
|
-
| magic loop (`for i in range(x.shape[0])`, `enumerate(x)`, `np.ndindex`/`np.ndenumerate`) | runs the function once: the loop already covers every element. A `(
|
|
249
|
-
| gid-style (`
|
|
250
|
-
| gid-style, domain from a `k[(
|
|
302
|
+
| magic loop (`for i in range(x.shape[0])`, `enumerate(x)`, `np.ndindex`/`np.ndenumerate`) | runs the function once: the loop already covers every element. A `(blocks, threads)` bracket changes nothing here, because the loop, not the launch, decides the extent |
|
|
303
|
+
| gid-style (`get_global_id`), domain from `@kernel(domain="x")` | runs the body once per work item of the declared domain (`x`'s full shape), with `get_global_id` reporting the current index — one serial step per element, in `np.ndindex` order |
|
|
304
|
+
| gid-style, domain from a `k[(blocks, threads)]` bracket | same, one step per work item of the bracket, so the body must clamp itself against the real element count exactly as it does on a GPU |
|
|
251
305
|
|
|
252
|
-
Inside such a body
|
|
306
|
+
Inside such a body the same functions resolve to their host implementations: the indexing builtins, the atomics (plain read-modify-writes — serial execution makes them exact), `barrier()` (a no-op) and the vector math family, which uses `pygpgpu.funcs`' host implementation. Two things cannot be emulated and raise instead: `local` (group-local memory is shared by parallel work items) and a `get_global_id` call inside a magic-loop body, where there is no current work item — use the loop variable there.
|
|
253
307
|
|
|
254
308
|
Vector-typed arrays work unchanged: reading `v = points[i]` yields a vector instance, and `out[i] = v.xyzy * 2.0` writes its components.
|
|
255
309
|
|
|
310
|
+
### One body, two behaviours: `KERNEL_TRANSLATING`
|
|
311
|
+
|
|
312
|
+
The interpreter runs the body as written, so a kernel that needs *different* code on each side has nowhere to put the difference — and a plain `if` on a value the device cannot see would be refused. `KERNEL_TRANSLATING` is the escape hatch, borrowed from `typing.TYPE_CHECKING`: it is `False` in Python and `True` while the body is being translated, so the condition is decided at **translation time** and exactly one branch is emitted:
|
|
313
|
+
|
|
314
|
+
```python
|
|
315
|
+
@kernel(domain="out")
|
|
316
|
+
def measure(data: np.ndarray, out: np.ndarray):
|
|
317
|
+
i = get_global_id(0)
|
|
318
|
+
if KERNEL_TRANSLATING:
|
|
319
|
+
out[i] = data[i] * 2.0 # device: the real computation
|
|
320
|
+
else:
|
|
321
|
+
out[i] = len(str(i)) # python: a host-only expression the device could not run
|
|
322
|
+
```
|
|
323
|
+
|
|
324
|
+
The branch is inlined as a macro — no `if` survives into the generated source — and the dropped branch is never translated, so Python-only work costs the device nothing. Only the name is recognized, so a kernel may declare its own `KERNEL_TRANSLATING` instead of importing it.
|
|
325
|
+
|
|
256
326
|
### Handwritten kernel files
|
|
257
327
|
|
|
258
328
|
Already have C-style kernels? `.cl` / `.cu` / `.hip` files (native syntax of the matching backend) import like Python modules. Write a `vecadd.cl`:
|
|
@@ -268,14 +338,16 @@ __kernel void vecadd(__global const float* a, __global const float* b,
|
|
|
268
338
|
```
|
|
269
339
|
|
|
270
340
|
```python
|
|
271
|
-
from pygpgpu import
|
|
341
|
+
from pygpgpu import kernel_import
|
|
272
342
|
import numpy as np
|
|
273
|
-
|
|
343
|
+
|
|
344
|
+
kernel_import.install()
|
|
345
|
+
import vecadd # one .cl file = a module with one backend
|
|
274
346
|
|
|
275
347
|
a = np.random.rand(1_000_000).astype(np.float32)
|
|
276
348
|
out = np.zeros_like(a)
|
|
277
|
-
vecadd.vecadd(a, a, out, np.int32(len(a)))
|
|
278
|
-
vecadd.vecadd[(
|
|
349
|
+
vecadd.vecadd(a, a, out, np.int32(len(a))) # work size inferred
|
|
350
|
+
vecadd.vecadd[(3907, 256)](a, a, out, np.int32(len(a))) # (blocks, threads)
|
|
279
351
|
```
|
|
280
352
|
|
|
281
353
|
Note the `n` argument: a handwritten kernel must clamp against the real element count, because the
|
|
@@ -284,6 +356,114 @@ size, not the buffer length, so it is not a usable bound.
|
|
|
284
356
|
|
|
285
357
|
Struct definitions map to ctypes types; a `.pyi` stub is generated next to the kernel so IDE completion works.
|
|
286
358
|
|
|
359
|
+
### One kernel, several backends
|
|
360
|
+
|
|
361
|
+
Same-named `.cl` / `.cu` / `.hip` files are ONE module. Put `vecops.cl`, `vecops.cu` and
|
|
362
|
+
`vecops.hip` in a package directory (with an `__init__.py`), then:
|
|
363
|
+
|
|
364
|
+
```python
|
|
365
|
+
from pygpgpu import kernel_import
|
|
366
|
+
|
|
367
|
+
kernel_import.install()
|
|
368
|
+
from kernel_import import vecops # the package that holds the three sources
|
|
369
|
+
|
|
370
|
+
vecops.add_offset(a, b, out, offset, n) # default device, like a @kernel call
|
|
371
|
+
vecops.add_offset["cuda"](a, b, out, ...) # a bracket names the backend
|
|
372
|
+
vecops.add_offset["opencl:1"][(blocks, threads)](...) # device + work size, either order
|
|
373
|
+
```
|
|
374
|
+
|
|
375
|
+
What makes one module out of three files:
|
|
376
|
+
|
|
377
|
+
* a kernel declared with the **same parameter list** in several files is one function with one
|
|
378
|
+
implementation per backend -- the bracket picks the implementation, the call keeps the kernel's
|
|
379
|
+
own signature;
|
|
380
|
+
* declared with **different parameter lists** it is a set of overloads -- the stub declares one
|
|
381
|
+
`@overload` per list, and the argument types pick the implementation at call time (a bare call
|
|
382
|
+
walks the same `cuda -> hip -> metal -> opencl` probe a `@kernel` uses);
|
|
383
|
+
* declared in only one file it still exists, and a bracket naming a backend without an
|
|
384
|
+
implementation raises `KeyError` at the bracket.
|
|
385
|
+
|
|
386
|
+
A `.pyi` stub is generated for the merged module: one `class Kernel_<name>` per kernel, one
|
|
387
|
+
`__call__` per distinct parameter list, and the structs from the first file that declares each.
|
|
388
|
+
|
|
389
|
+
### The scientific-computing vocabulary
|
|
390
|
+
|
|
391
|
+
The device-side math functions are plain names, like every other builtin:
|
|
392
|
+
|
|
393
|
+
```python
|
|
394
|
+
from pygpgpu import erf, tgamma, fast_exp, normal_cdf
|
|
395
|
+
|
|
396
|
+
@kernel
|
|
397
|
+
def integrate(x: np.ndarray, out: np.ndarray):
|
|
398
|
+
for i in range(out.shape[0]):
|
|
399
|
+
out[i] = erf(x[i]) * tgamma(x[i] + 1.0)
|
|
400
|
+
```
|
|
401
|
+
|
|
402
|
+
Three groups, and the difference matters:
|
|
403
|
+
|
|
404
|
+
* the functions **all three backends have** -- `erf`, `erfc`, `tgamma`, `lgamma`, `rint`,
|
|
405
|
+
`remainder`, `fmin`, `fmax`, `fdim`, `copysign`, `nextafter`, `ldexp`, `fract`, `sign`,
|
|
406
|
+
and the `native_*` family. These run on OpenCL, CUDA and HIP alike.
|
|
407
|
+
* the functions **CUDA and HIP have** and OpenCL does not -- the fast approximations
|
|
408
|
+
(`fast_exp`, `fast_log`, `fast_sin`, ...), the correctly-rounded forms (`exact_recip`,
|
|
409
|
+
`exact_divide`, `exact_sqrt`, ...), the special functions (`erf_inverse`, `normal_cdf`,
|
|
410
|
+
`reciprocal_hypot`, `sin_pi`, `cos_pi`, the Bessel functions) and the conversions
|
|
411
|
+
(`float_to_int_nearest`, `int_to_float`, ...). A kernel that uses one of these is a
|
|
412
|
+
**CUDA or HIP kernel**: on OpenCL the translator refuses it *by name*, so the message
|
|
413
|
+
says which function has no OpenCL counterpart rather than leaving you with an
|
|
414
|
+
"undeclared identifier" from the compiler.
|
|
415
|
+
* the `mad` / `fma` family and the vector operations, which pygf already publishes and
|
|
416
|
+
which are pygf's own objects here (see `pygpgpu.funcs`).
|
|
417
|
+
|
|
418
|
+
CUDA and HIP answer the same thing by the same name, but they are not the same device and
|
|
419
|
+
the difference is worth knowing:
|
|
420
|
+
|
|
421
|
+
* HIP has no `__exp2f`, so `native_exp2` is spelled `exp2f` there -- the ordinary name,
|
|
422
|
+
which HIP does have.
|
|
423
|
+
* HIP has **none** of the round-toward-zero intrinsics (`__frcp_rz`, `__fdiv_rz`,
|
|
424
|
+
`__fsqrt_rz`), so `exact_recip_rz`, `exact_divide_rz` and `exact_sqrt_rz` are computed
|
|
425
|
+
on HIP by a device helper the package emits: it takes the correctly rounded value, uses
|
|
426
|
+
the fused multiply-add to learn which side of it the exact result is on, and steps one
|
|
427
|
+
unit toward zero when it is the other way. The result agrees with CUDA's own intrinsic
|
|
428
|
+
bit for bit (a division is the one exception, because the two hardware divide units
|
|
429
|
+
round differently in the last bit -- the rounding *direction* is the same either way).
|
|
430
|
+
|
|
431
|
+
Two names are worth a warning, because the device's documentation is the authority and
|
|
432
|
+
the spelling does not tell you:
|
|
433
|
+
|
|
434
|
+
* CUDA's `rhypotf` is the **reciprocal** of the hypotenuse (`1/sqrt(x²+y²)`), so the name
|
|
435
|
+
here is `reciprocal_hypot`; the plain hypotenuse is `hypot`.
|
|
436
|
+
* CUDA's `sinpif` is `sin(πx)`, **not** the sinc function -- so the name here is `sin_pi`.
|
|
437
|
+
|
|
438
|
+
### Naming a device function that already exists
|
|
439
|
+
|
|
440
|
+
A device function that is not one of the builtins -- a `__device__` function in a header
|
|
441
|
+
you ship, or a compiler intrinsic with a spelling of your own -- is registered:
|
|
442
|
+
|
|
443
|
+
```python
|
|
444
|
+
from pygpgpu import device_function
|
|
445
|
+
|
|
446
|
+
@device_function(cuda="my_fast_rcp", opencl="native_recip",
|
|
447
|
+
headers={"cuda": "myclib.h"})
|
|
448
|
+
def my_fast_rcp(x: float) -> float:
|
|
449
|
+
"""The host's implementation; the device gets the symbol the registry names."""
|
|
450
|
+
return 1.0 / x
|
|
451
|
+
|
|
452
|
+
@kernel
|
|
453
|
+
def use_it(x: np.ndarray, out: np.ndarray):
|
|
454
|
+
for i in range(out.shape[0]):
|
|
455
|
+
out[i] = my_fast_rcp(x[i])
|
|
456
|
+
```
|
|
457
|
+
|
|
458
|
+
The device calls the symbol the table names; the header is copied into the cache and
|
|
459
|
+
included in the generated source, so you do not have to know where the cache is (a header
|
|
460
|
+
given as a full `#include` line is carried into the source verbatim instead, which is what
|
|
461
|
+
a system header such as `#include <cuda_fp16.h>` needs). A backend with no symbol for the
|
|
462
|
+
function is refused by name, the same way the CUDA-only builtins are.
|
|
463
|
+
|
|
464
|
+
The body is the **host's** implementation -- the `python` backend runs it -- so the two
|
|
465
|
+
sides have to agree, exactly as they do for the builtins.
|
|
466
|
+
|
|
287
467
|
### Class methods
|
|
288
468
|
|
|
289
469
|
```python
|
|
@@ -459,7 +639,7 @@ wp.launch(saxpy, dim=n, inputs=[x, y, out, 2.0])
|
|
|
459
639
|
| Input data | numpy arrays directly | wrap in `ti.field`, explicit `from_numpy`/`to_numpy` | `cuda.to_device` + `copy_to_host` by hand | wrap in `wp.array` |
|
|
460
640
|
| Helper functions | **plain Python — no decorator, annotations optional** | `@ti.func` + annotated args | `@cuda.jit(device=True)` | `@wp.func` + annotated args |
|
|
461
641
|
| Backends in one process | **all three, mixed freely** | one, fixed by `ti.init(arch=...)` | CUDA only | CUDA only |
|
|
462
|
-
| Launch size | inferred from the loop bound (or `
|
|
642
|
+
| Launch size | inferred from the loop bound (or `domain="x"` / a bracket) | inferred from `range` | blocks × threads by hand, plus a guard | pass `dim=` |
|
|
463
643
|
| Bounds guard | emitted for you | emitted for you | you write it | you write it |
|
|
464
644
|
| Backends | OpenCL, CUDA, HIP | CPU, CUDA, Vulkan and others | CPU, CUDA | CPU, CUDA |
|
|
465
645
|
| AMD GPU | HIP (native AMD codegen) and OpenCL | via Vulkan | no | no |
|
|
@@ -484,7 +664,7 @@ apply_all["cuda"](x, out) # NVIDIA
|
|
|
484
664
|
apply_all["hip"](x, out) # AMD
|
|
485
665
|
apply_all["opencl"](x, out) # whichever device OpenCL enumerates
|
|
486
666
|
apply_all["python"](x, out) # the interpreter, on the CPU (no device needed)
|
|
487
|
-
apply_all(x, out) # unqualified: probe cuda -> hip -> opencl
|
|
667
|
+
apply_all(x, out) # unqualified: probe cuda -> hip -> metal -> opencl
|
|
488
668
|
```
|
|
489
669
|
|
|
490
670
|
Each backend/device pair is specialized and cached separately, so alternating between them is just
|
|
@@ -496,11 +676,86 @@ Where the others are ahead — worth knowing before you pick:
|
|
|
496
676
|
- **Automatic differentiation**: Taichi and Warp can differentiate kernels; this library cannot.
|
|
497
677
|
- **Sparse and irregular data structures**: Taichi has sparse grids and SNode trees; here you pass dense numpy arrays (or handle sparsity yourself).
|
|
498
678
|
- **CPU fallback**: all three can run on the CPU; this library runs on the CPU only through the explicit `k["python"]` interpreter backend, which is serial and meant for testing rather than throughput.
|
|
499
|
-
- **Minimum interpreter version**: this library runs on Python 3.7 and newer, on
|
|
679
|
+
- **Minimum interpreter version**: this library runs on Python 3.7 and newer, on 64- and 32-bit Windows and on Linux x86-64; the others require a newer interpreter. Linux is exercised end to end under WSL (CUDA through the `/usr/lib/wsl` driver passthrough, OpenCL through a runtime the distribution ships), so the driver-name and library-discovery paths there are the real ones rather than an untested branch.
|
|
500
680
|
|
|
501
681
|
This table was written from each project's documentation and the snippets above; their APIs move
|
|
502
682
|
fast, so confirm against current docs before relying on a specific cell.
|
|
503
683
|
|
|
684
|
+
### Return values and outer variables
|
|
685
|
+
|
|
686
|
+
A kernel runs once per work item, so there is no single C return value to hand back. The
|
|
687
|
+
translator gives every returned position a device parameter of its own — a one-element
|
|
688
|
+
buffer for a scalar or vector, the array object itself for an array — and the first work
|
|
689
|
+
item to reach a `return` claims the result with an atomic compare-and-swap. The host reads
|
|
690
|
+
the slots back after the launch and returns exactly what the winning work item returned:
|
|
691
|
+
|
|
692
|
+
```python
|
|
693
|
+
@kernel
|
|
694
|
+
def first_peak(x: np.ndarray):
|
|
695
|
+
for i in range(x.shape[0]):
|
|
696
|
+
if x[i] > 10.0:
|
|
697
|
+
return x[i], i # a tuple
|
|
698
|
+
return None # a launch where nothing returns is None
|
|
699
|
+
|
|
700
|
+
value, index = first_peak[backend](x)
|
|
701
|
+
```
|
|
702
|
+
|
|
703
|
+
The supported return types are exactly the supported parameter types, every `return`
|
|
704
|
+
statement may have its own shape (`return x[i]` from one branch, `return x[i], i` from
|
|
705
|
+
another), and a bare `return` gives `None`. `submit()` and `async_call()` deliver the same
|
|
706
|
+
value once their future resolves, after read-back. A value-returning `return` is refused
|
|
707
|
+
below the magic loop: there it would return one work item's local, not the function's
|
|
708
|
+
result.
|
|
709
|
+
|
|
710
|
+
Variables the body reads from outside become parameters too, so a changed outer value is
|
|
711
|
+
visible on the next call — inlining it would leave the kernel stale:
|
|
712
|
+
|
|
713
|
+
```python
|
|
714
|
+
SCALE = 2.0
|
|
715
|
+
|
|
716
|
+
@kernel
|
|
717
|
+
def scaled(x: np.ndarray, out: np.ndarray):
|
|
718
|
+
for i in range(out.shape[0]):
|
|
719
|
+
out[i] = x[i] * SCALE
|
|
720
|
+
|
|
721
|
+
scaled[backend](x, out) # SCALE is passed as an argument
|
|
722
|
+
SCALE = 100.0 # the next call uses 100.0
|
|
723
|
+
scaled[backend](x, out)
|
|
724
|
+
```
|
|
725
|
+
|
|
726
|
+
A `nonlocal`/`global` declaration makes the outer scalar writable: it travels as a pointer,
|
|
727
|
+
every use inside is a dereference, and the value is copied back into its scope after the
|
|
728
|
+
call — a closure cell for `nonlocal`, the module globals for `global`. Outer arrays are
|
|
729
|
+
pointer parameters as well, and are read back when the kernel writes them. Rebinding an
|
|
730
|
+
outer array (as opposed to writing its elements) is refused, because the object is shared
|
|
731
|
+
with the caller. A helper function gets the same treatment as the kernel that calls it.
|
|
732
|
+
|
|
733
|
+
Statements above the magic loop may also *create* data with numpy — `np.zeros`, `np.ones`,
|
|
734
|
+
`np.empty`, `np.full`, `np.arange`, `np.linspace`, `*_like`, `np.float32(...)` and the rest
|
|
735
|
+
of the allocating constructors. The interpreter runs those statements, so the array has
|
|
736
|
+
exactly the shape and dtype numpy gives it, and it reaches the kernel as a parameter:
|
|
737
|
+
|
|
738
|
+
```python
|
|
739
|
+
@kernel
|
|
740
|
+
def normalized(x: np.ndarray, out: np.ndarray):
|
|
741
|
+
total = np.zeros(1, dtype=np.float32) # built on the host
|
|
742
|
+
for i in range(x.shape[0]):
|
|
743
|
+
total[0] = total[0] + x[i]
|
|
744
|
+
for i in range(out.shape[0]):
|
|
745
|
+
out[i] = x[i] / total[0]
|
|
746
|
+
|
|
747
|
+
@kernel
|
|
748
|
+
def doubled(x: np.ndarray):
|
|
749
|
+
result = np.zeros_like(x) # created, then returned as itself
|
|
750
|
+
for i in range(x.shape[0]):
|
|
751
|
+
result[i] = x[i] * 2.0
|
|
752
|
+
return result
|
|
753
|
+
```
|
|
754
|
+
|
|
755
|
+
A created object the body returns reuses the parameter it already travels through, so the
|
|
756
|
+
caller receives the host's own object rather than a copy. Creation below the magic loop is
|
|
757
|
+
refused: it would build one object per work item instead of one.
|
|
758
|
+
|
|
504
759
|
### Images
|
|
505
760
|
|
|
506
761
|
`Image` is one image type for every backend. A kernel indexes it like an array, and the
|
|
@@ -561,6 +816,97 @@ The HIP backend refuses images: it reuses CUDA code generation, which has no tex
|
|
|
561
816
|
object type, and the driver rejects `hipTexObjectCreate` on hardware without texture
|
|
562
817
|
support.
|
|
563
818
|
|
|
819
|
+
## Host-side CUDA libraries
|
|
820
|
+
|
|
821
|
+
The CUDA *driver* (`pygpgpu/cuda/driver`) is the low-level API: contexts, streams,
|
|
822
|
+
modules, memory. The CUDA *math libraries* ship as separate DLLs next to it and do
|
|
823
|
+
the actual mathematics. They are bound with ctypes under `pygpgpu/cuda/libraries`, in
|
|
824
|
+
the same shape as the driver — a lazily loaded DLL, a signature table per library, and
|
|
825
|
+
an error code turned into a `RuntimeError` carrying the library's own message:
|
|
826
|
+
|
|
827
|
+
```python
|
|
828
|
+
from pygpgpu.cuda.libraries import driver_for
|
|
829
|
+
|
|
830
|
+
CUBLAS = driver_for("cublas") # the cuBLAS entry points
|
|
831
|
+
CUFFT = driver_for("cufft") # the cuFFT entry points
|
|
832
|
+
```
|
|
833
|
+
|
|
834
|
+
| Library | Bound operations |
|
|
835
|
+
|---|---|
|
|
836
|
+
| **cuBLAS** | `gemm` (float32/float64), `axpy`, `scal`, `dot`, `GemmEx`, handle and stream management |
|
|
837
|
+
| **cuBLASLt** | the generic `matmul` with a heuristically chosen algorithm, matrix layouts, epilogues |
|
|
838
|
+
| **cuFFT** | `plan1d`, `exec` (R2C / C2R / C2C), plan destruction, stream binding |
|
|
839
|
+
| **cuSPARSE** | `csrmv` (createCsr / createDnVec / SpMV), dense and sparse descriptors |
|
|
840
|
+
| **cuSOLVER** | `potrf` (Cholesky), `gesvd` (SVD), `getrf` (LU), with their workspace queries |
|
|
841
|
+
| **CUDA runtime** | `cudaMalloc`, `cudaFree`, `cudaMemcpy`, `cudaMemset`, the device count and properties |
|
|
842
|
+
|
|
843
|
+
A thin, numpy-friendly layer sits on top of the raw entry points, so a call takes
|
|
844
|
+
arrays rather than device pointers:
|
|
845
|
+
|
|
846
|
+
```python
|
|
847
|
+
import numpy as np
|
|
848
|
+
from pygpgpu.cuda_libraries import gemm, axpy, dot, rfft, csrmv
|
|
849
|
+
|
|
850
|
+
a = np.random.rand(3, 4).astype(np.float32)
|
|
851
|
+
b = np.random.rand(4, 2).astype(np.float32)
|
|
852
|
+
|
|
853
|
+
c = gemm(a, b) # a @ b, on the GPU
|
|
854
|
+
d = axpy(2.0, a[0], b[0]) # elementwise
|
|
855
|
+
e = dot(a[0], b[0]) # a scalar
|
|
856
|
+
f = rfft(a[0]) # n/2+1 complex numbers, like np.fft.rfft
|
|
857
|
+
g = csrmv(row_offsets, column_indices, values, x) # a sparse product
|
|
858
|
+
```
|
|
859
|
+
|
|
860
|
+
The same names are reachable as attributes of the module, so both spellings work:
|
|
861
|
+
|
|
862
|
+
```python
|
|
863
|
+
from pygpgpu import cuda_libraries
|
|
864
|
+
cuda_libraries.gemm(a, b)
|
|
865
|
+
|
|
866
|
+
from pygpgpu.cuda_libraries import gemm
|
|
867
|
+
gemm(a, b)
|
|
868
|
+
```
|
|
869
|
+
|
|
870
|
+
The wrappers take numpy's **row-major** layout and return the same layout; cuBLAS
|
|
871
|
+
reads column-major, and the difference is handled inside, so `gemm(a, b)` is just
|
|
872
|
+
`a @ b`. The 1-D cases follow numpy's own rules for `@`, so a matrix and a vector
|
|
873
|
+
multiply without the caller reshaping anything.
|
|
874
|
+
|
|
875
|
+
**A CUDA context has to be current.** A library handle belongs to the context that
|
|
876
|
+
was current when it was created, so the wrappers make one current if none is — but
|
|
877
|
+
if you use the raw entry points, make one current yourself first:
|
|
878
|
+
|
|
879
|
+
```python
|
|
880
|
+
from pygpgpu import get_device
|
|
881
|
+
|
|
882
|
+
device = get_device("cuda:0")
|
|
883
|
+
device.default_context.make_current() # before the first library call
|
|
884
|
+
```
|
|
885
|
+
|
|
886
|
+
Without it the call fails with `CUDA_ERROR_INVALID_CONTEXT`, and the message says
|
|
887
|
+
so.
|
|
888
|
+
|
|
889
|
+
**These are host-side operations, not device builtins.** A `gemm` moves whole arrays
|
|
890
|
+
to the device and runs one library call; a `@kernel` body is per-thread arithmetic.
|
|
891
|
+
That is why a `@kernel` cannot call `cuda_libraries.gemm` — the two are different
|
|
892
|
+
layers, and mixing them would mean a kernel launching a library call per work item.
|
|
893
|
+
The device-side counterparts (a per-element multiply, a per-element dot) are the
|
|
894
|
+
device builtins the top level already exports.
|
|
895
|
+
|
|
896
|
+
The libraries are **loaded lazily**: `import pygpgpu` loads no CUDA library at all,
|
|
897
|
+
and each library's DLL is loaded on the first call that needs it. A machine without
|
|
898
|
+
the CUDA toolkit still imports the package, and the failure surfaces at the call with
|
|
899
|
+
the loader's own error.
|
|
900
|
+
|
|
901
|
+
**cuSOLVER needs kernels the consumer cards do not have.** NVIDIA does not ship
|
|
902
|
+
cuSOLVER's dense factorization kernels for every architecture, and a card without
|
|
903
|
+
them fails at execution rather than at setup: the call returns a failure code and the
|
|
904
|
+
context reports an illegal memory access. Measured on the machine this was developed
|
|
905
|
+
on (an RTX 4070 Laptop, compute capability 8.9, CUDA 12.6), `potrf`, `getrf`,
|
|
906
|
+
`gesvd`, `gesvdj` and `syevj` all fail that way, with `compute-sanitizer` putting the
|
|
907
|
+
fault inside cuSOLVER's own DLL. The bindings are correct and the test skips with
|
|
908
|
+
that reason instead of failing.
|
|
909
|
+
|
|
564
910
|
## Device selection
|
|
565
911
|
|
|
566
912
|
Three forms of device identifier inside the brackets (same on every backend):
|
|
@@ -583,7 +929,7 @@ dev = get_device("cuda:0") # the Device object of that backend, directly
|
|
|
583
929
|
|
|
584
930
|
### Choosing the default device
|
|
585
931
|
|
|
586
|
-
A call without a bracket runs on the first usable backend in `cuda -> hip -> opencl`.
|
|
932
|
+
A call without a bracket runs on the first usable backend in `cuda -> hip -> metal -> opencl`.
|
|
587
933
|
`set_default_device()` replaces that order:
|
|
588
934
|
|
|
589
935
|
```python
|
|
@@ -609,6 +955,7 @@ is. A kernel decorated with an explicit `@kernel(backend=...)` keeps that backen
|
|
|
609
955
|
| OpenCL | `__clcache__/` | parsed meta + per-device binaries |
|
|
610
956
|
| CUDA | `__cucache__/` | parsed meta + per-device cubin/ptx |
|
|
611
957
|
| HIP | `__hipcache__/` | parsed meta + per-device code objects |
|
|
958
|
+
| Metal | `__metalcache__/` | generated MSL source, compiled at first launch |
|
|
612
959
|
| Python | none | nothing is generated or compiled; the body runs as-is |
|
|
613
960
|
|
|
614
961
|
Caches are invalidated by the mtime of the source file (and every `#include`); sources generated by `@kernel` are keyed by a hash of the Python source. These directories should be in `.gitignore`.
|
|
@@ -637,15 +984,15 @@ set_print_info(True) # verbose again (the default)
|
|
|
637
984
|
|
|
638
985
|
## Known limitations
|
|
639
986
|
|
|
640
|
-
- `@kernel` does not support: strings/lists/dicts,
|
|
987
|
+
- `@kernel` does not support: strings/lists/dicts, recursion, dynamic multi-dimensional shape indexing. Closures and module globals *are* supported: an outer variable becomes a kernel parameter, and one declared `nonlocal`/`global` is written back after the call (see "Return values and outer variables" above);
|
|
641
988
|
- `print()` inside a kernel prints one `True`/`False` pair, integer, real or vector at a time. `sep` and `end` are translated, but must be string literals (the format string is fixed in the device code); `file` and `flush` are refused; it also cannot print an array (print an element instead). A line containing many floats expands into a `switch` over their digit counts, so an extremely long line can hit the code-size guard and should be split into several calls. The HIP backend rejects `print()` outright: its driver drops the device on such kernels on the tested runtime;
|
|
642
989
|
- vector comparison, writing through a swizzle, and nesting beyond three operands are restricted in favour of cross-backend consistency (the error message suggests a workaround);
|
|
643
|
-
- `Image` is unsupported on the HIP backend: HIP reuses CUDA code generation, which has no texture object type, and `hipTexObjectCreate` is rejected with "operation not supported" on hardware without texture support (an integrated Radeon under ROCm 7.2 Windows, for instance). The translator raises a `
|
|
990
|
+
- `Image` is unsupported on the HIP backend: HIP reuses CUDA code generation, which has no texture object type, and `hipTexObjectCreate` is rejected with "operation not supported" on hardware without texture support (an integrated Radeon under ROCm 7.2 Windows, for instance). The translator raises a `SyntaxError` naming the backend; use cuda, opencl or python for image kernels;
|
|
644
991
|
- type annotations on a `@kernel` function are accepted and ignored: the device type comes from the actual argument at call time, since one kernel is specialized per argument-type signature. Annotations are there for editors and type checkers, not for translation;
|
|
645
992
|
- a `@kernel` signature may carry default values, and a call may pass arguments by keyword: the call is bound to positional order before launch, with omitted defaults filled in. `*args`, `**kwargs` and keyword-only parameters remain unsupported;
|
|
646
993
|
- fp16 (`np.float16`) arguments are unavailable on the CUDA/HIP backends (use float32);
|
|
647
994
|
- the HIP backend compiles with the clang shipped by the AMD HIP SDK; hipRTC on this machine (ROCm 7.2 Windows) fails on any input, so it is not used;
|
|
648
|
-
- the pure-Python backend (`k["python"]`) does not emulate `
|
|
995
|
+
- the pure-Python backend (`k["python"]`) does not emulate `local` (group-shared memory): it runs serially and has no parallel work items to share it.
|
|
649
996
|
|
|
650
997
|
On CUDA, the array APIs (`Array` upload/read-back through `cuMemcpy2D`) work on every supported interpreter. `cuSurfObjectCreate` and `cuTexObjectCreate` occasionally return `CUDA_ERROR_INVALID_VALUE` on a descriptor that is byte-for-byte valid; the failures cluster in the first few calls of a process and then stop. Measured on the machine this was developed on (driver 13.4, RTX 4070): about one in a hundred creations fails, retrying an already-failed descriptor does not recover it, and a fresh descriptor for a newly created array usually does. Because an `Image` binds one surface and one texture per launch, a scenario that launches several image kernels in one process can therefore lose a few launches. The `python` and OpenCL backends are unaffected: OpenCL images are created per launch on a code path that has not shown the failure, and the `python` backend touches no device at all. `examples/11_texture2d.py` reports a driver refusal instead of raising.
|
|
651
998
|
## License
|