英文:
How to construct and access libcu++' <cuda/std/mdspan> on the host
问题
libcu++ 2.1.0 包含了针对 nvcc 支持的 C++14 及更高版本的 mdspan 实现。
我尝试使用 nvcc 12.0 和 libcu++ 2.1.0 实现了来自 cppreference(https://en.cppreference.com/w/cpp/container/mdspan)的 mdspan 示例代码。
我注意到了两个问题。
首先,我无法像示例中那样以相同的方式构造 mdspan。
>没有与参数列表匹配的“mdspan”构造函数实例
参数类型为:(int*,int,int)
其次,通过 operator[] 访问 mdspan 不会编译通过。
>错误:没有匹配这些操作数的运算符“[]”
操作数类型为:cuda::std::__4::mdspan<int,cuda::std::__4::extents<std::size_t,2UL,3UL,2UL>,cuda::std::__4::layout_right,cuda::std::__4::default_accessor<int>> [std::size_t]
1. 如何在构造函数中指定在编译时未知的 extent?
2. 如何访问 mdspan 的数据?
以下是我的代码,使用 nvcc -Ilibcudacxx-2.1.0/include/ -std=c++17 main.cu -o main
编译时无法通过:
#include <cstddef>
#include <vector>
#include <cstdio>
#include <cuda/std/mdspan>
int main()
{
std::vector v{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12};
// 错误:没有与参数列表匹配的“mdspan”构造函数实例。参数类型为:(int*,int,int)
// auto ms2 = cuda::std::mdspan(v.data(), 2, 6);
// auto ms3 = cuda::std::mdspan(v.data(), 2, 3, 2);
// 编译时 extent 不会出错
cuda::std::mdspan<int, cuda::std::extents<std::size_t, 2,6>> ms2(v.data());
cuda::std::mdspan<int, cuda::std::extents<std::size_t, 2,3,2>> ms3(v.data());
// 使用 2D 视图写入数据
for (std::size_t i = 0; i != ms2.extent(0); i++)
for (std::size_t j = 0; j != ms2.extent(1); j++)
// 没有匹配这些操作数的运算符“[]”。操作数类型为:cuda::std::__4::mdspan<int, cuda::std::__4::extents<std::size_t, 2UL, 6UL>, cuda::std::__4::layout_right, cuda::std::__4::default_accessor<int>> [std::size_t]
ms2[i, j] = i * 1000 + j;
// 使用 3D 视图读取数据
for (std::size_t i = 0; i != ms3.extent(0); i++)
{
printf("在 i = %lu 时的切片\n", i);
for (std::size_t j = 0; j != ms3.extent(1); j++)
{
for (std::size_t k = 0; k != ms3.extent(2); k++)
printf("%d ", ms3[i, j, k]);
printf("\n");
}
}
}
希望这些信息能帮助你解决问题。
英文:
libcu++ 2.1.0 includes an mdspan implementation for nvcc for c++14 and later.
I tried to implement the mdspan sample code from cppreference (https://en.cppreference.com/w/cpp/container/mdspan) with nvcc 12.0 and libcu++ 2.1.0
I noticed two problems.
First, I am unable to construct the mdspan in the same way as in the example.
>no instance of constructor "mdspan" matches the argument list
argument types are: (int *, int, int)
Second, the access to mdspan via operator[] does not compile
> error: no operator "[]" matches these operands
operand types are: cuda::std::__4::mdspan<int, cuda::std::__4::extents<std::size_t, 2UL, 3UL, 2UL>, cuda::std::__4::layout_right, cuda::std::__4::default_accessor<int>> [ std::size_t ]
1. How does one specify an extent in the constructor which is not known at compile-time?
2. How to access the data of mdspan?
Below is my code which does not compile with nvcc -Ilibcudacxx-2.1.0/include/ -std=c++17 main.cu -o main
.
#include <cstddef>
#include <vector>
#include <cstdio>
#include <cuda/std/mdspan>
int main()
{
std::vector v{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12};
//error: no instance of constructor "mdspan" matches the argument list. argument types are: (int *, int, int)
//auto ms2 = cuda::std::mdspan(v.data(), 2, 6);
//auto ms3 = cuda::std::mdspan(v.data(), 2, 3, 2);
//no compilation error with compile-time extents
cuda::std::mdspan<int, cuda::std::extents<std::size_t, 2,6>> ms2(v.data());
cuda::std::mdspan<int, cuda::std::extents<std::size_t, 2,3,2>> ms3(v.data());
// write data using 2D view
for (std::size_t i = 0; i != ms2.extent(0); i++)
for (std::size_t j = 0; j != ms2.extent(1); j++)
//no operator "[]" matches these operands. operand types are: cuda::std::__4::mdspan<int, cuda::std::__4::extents<std::size_t, 2UL, 6UL>, cuda::std::__4::layout_right, cuda::std::__4::default_accessor<int>> [ std::size_t ]
ms2[i, j] = i * 1000 + j;
// read back using 3D view
for (std::size_t i = 0; i != ms3.extent(0); i++)
{
printf("slice @ i = %lu\n", i);
for (std::size_t j = 0; j != ms3.extent(1); j++)
{
for (std::size_t k = 0; k != ms3.extent(2); k++)
printf("%d ", ms3[i, j, k]);
printf("\n");
}
}
}
</details>
# 答案1
**得分**: 3
`mdspan`的`libcu++`实现基于Kokkos的参考实现,Kokkos在自述文件中有一些注意事项:
> 此实现与C++23草案标准中于2022年7月投票通过的`mdspan`版本完全一致。
当不处于C++23模式时,该实现与提案有以下不同:
>
> ### C++20
> - 实现了`operator()`而不是`operator[]`
> - 请注意,您可以通过定义`MDSPAN_USE_BRACKET_OPERATOR=[0,1]`和`MDSPAN_USE_PAREN_OPERATOR=[0,1]`来控制可用的运算符,而不管是否检测到多维子脚本支持。
>
> ### C++17
> - 即使在不应该的情况下(即所有静态范围和默认可构造的映射/访问器),mdspan都有默认构造函数。
> - 缺少条件显式标记,使某些构造函数变得隐式
> - 特别是您可以隐式地从动态范围转换为静态范围,在C++20模式下不能这样做。
> - 对于`layout_left::mapping::stride()`、`layout_right::mapping::stride()`和`layout_stride::mapping::stride()`存在一个约束条件,即`extents_type::rank() > 0`为`true`,而在C++17或C++14中没有实现。
>
> ### C++14
> - 没有模板参数推导指南
> - submdspan(P2630)不可用 - 在C++14模式下,可以使用早期版本的submdspan直到0.5版本
> - 没有可用的基准测试(它们需要submdspan)
参考实现之所以使用`operator()`而不是`operator[]`(请参阅上面的“C++20”部分),是因为**带有多个参数的`operator[]`是C++23的特性**,因此无法移植到较早的C++版本中。
示例中使用的构造函数适用于C++17及更高版本(C++14没有必要的CTAD*/模板参数推导指南),或者至少它们对其进行了[测试][4]。对于某些(主机)编译器/版本,CTAD可能会被[禁用][5],但也有[另一种方法][6]可以在没有CTAD的情况下指定动态范围:
```c++
constexpr auto dyn = cuda::std::dynamic_extent;
int main() {
...
using ext_t = cuda::std::extents<int, dyn, dyn>;
cuda::std::mdspan<int, ext_t> ms2{ v.data(), ext_t{2, 6} };
...
}
*CTAD:模板参数类推导(Class Template Argument Deduction)
英文:
The libcu++ implementation of mdspan
is based on the reference implementation by Kokkos which has some caveats in the readme:
> This implementation is fully conforming with the version of mdspan
voted into the C++23 draft standard in July 2022.
When not in C++23 mode the implementation deviates from the proposal as follows:
>
> ### C++20
> - implements operator()
not operator[]
> - note you can control which operator is available with defining MDSPAN_USE_BRACKET_OPERATOR=[0,1]
and MDSPAN_USE_PAREN_OPERATOR=[0,1]
irrespective of whether multi dimensional subscript support is detected.
>
> ### C++17
> - mdspan has a default constructor even in cases where it shouldn't (i.e. all static extents, and default constructible mapping/accessor)
> - the conditional explicit markup is missing, making certain constructors implicit
> - most notably you can implicitly convert from dynamic extent to static extent, which you can't in C++20 mode
> - there is a constraint on layout_left::mapping::stride()
, layout_right::mapping::stride()
and layout_stride::mapping::stride()
that extents_type::rank() > 0
is true
, which is not implemented in C++17 or C++14.
>
> ### C++14
> - deduction guides don't exist
> - submdspan (P2630) is not available - an earlier variant of submdspan is available up to release 0.5 in C++14 mode
> - benchmarks are not available (they need submdspan)
The reason for the reference implementation using operator()
instead of operator[]
(see section "C++20" above) is that operator[]
with multiple arguments is a C++23 feature so it can't be ported to earlier C++ versions.
The constructor that is used in the sample is implemented for C++17 and later (C++14 does not have the necessary CTAD*/deduction guides), or at least they have a test for it. CTAD might be turned off for some (host-) compilers/versions, but there is an alternative way of specifying dynamic extents without CTAD:
constexpr auto dyn = cuda::std::dynamic_extent;
int main() {
...
using ext_t = cuda::std::extents<int, dyn, dyn>;
cuda::std::mdspan<int, ext_t> ms2{ v.data(), ext_t{2, 6} };
...
}
*CTAD: Class Template Argument Deduction
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论