什么是 mdspan,它的用途是什么?

2024-01-20

在过去的一年左右的时间里,我注意到 StackOverflow 上有一些与 C++ 相关的答案,请参考mdspan's - 但我从未在 C++ 代码中真正见过这些。我尝试在我的 C++ 编译器的标准库目录和C++ 编码指南 http://isocpp.github.io/CppCoreGuidelines/CppCoreGuidelines- 但找不到他们。我did find std::span的;我猜它们是相关的——但是如何呢?添加“md”代表什么?

请解释一下这个神秘实体的用途,以及我何时需要使用它。


TL;DR: mdspan是一个扩展std::span对于多个维度 - 具有大量(不可避免的)灵活的可配置性。内存布局和访问模式。


在阅读此答案之前,您应该确保您清楚what a span是什么以及它的用途 https://stackoverflow.com/q/45723819/1593077。现在这已经不成问题了:因为mdspan可能是相当复杂的野兽(通常〜7x或更多源代码作为std::span实现),我们将从简化的描述开始,并在下面进一步保留高级功能。

“它是什么?” (简单版)

An mdspan<T> is:

  1. 从字面上看,一个“multi-d尺寸跨度”(类型-T元素)。
  2. 的概括std::span<T>,从一维/线性元素序列到多维。
  3. 类型元素的连续序列的非拥有视图T在内存中,解释为多维数组。
  4. 基本上只是一个struct { T * ptr; size_type extents[d]; }使用一些方便的方法(例如d尺寸在运行时确定)。

的插图mdspan-解释布局

如果我们有:

std::vector v = {1,2,3,4,5,6,7,8,9,10,11,12};

我们可以查看的数据v作为 12 个元素的一维数组,类似于其原始定义:

auto sp1 = std::span(v.data(), 12);
auto mdsp1 = std::mdspan(v.data(), 12);

或范围为 2 x 6 的二维数组:

auto mdsp2 = std::mdspan(v.data(), 2, 6 );
// (  1,  2,  3,  4,  5,  6 ),
// (  7,  8,  9, 10, 11, 12 )

或 3D 数组 2 x 3 x 2:

auto ms3 = std::mdspan(v.data(), 2, 3, 2);
// ( ( 1,  2 ), ( 3,  4 ), (  5,  6 ) ),
// ( ( 7,  8 ), ( 9, 10 ), ( 11, 12 ) )

我们也可以将其视为 3 x 2 x 2 或 2 x 2 x 3 数组,或 3 x 4 等等。

“我应该什么时候使用它?”

  • (C++23 及更高版本)当您想使用多维operator[]在你从某处获得的某个缓冲区上。因此在上面的例子中,ms3[1, 2, 0] is 11 and ms3[0, 1, 1] is 4 .

  • 当您想要传递多维数据而不分离原始数据指针和维度时。您已经在内存中获得了一堆元素,并且想要使用多个维度来引用它们。因此,而不是:

    void print_matrix_element(
       float const* matrix, size_t row_width, size_t x, size_t y) 
    {
       std::print("{}", matrix[row_width * x + y]);
    }
    

    你可以写:

    void print_matrix_element(
        std::mdspan<float const, std::dextents<size_t, 2>> matrix,
        size_t x, size_t y)
    {
       std::print("{}", matrix[x, y]);
    }
    
  • 作为传递多维 C 数组的正确类型:
    C支持多维数组 https://stackoverflow.com/questions/tagged/multidimensional-array完美......只要它们的维度在编译时给出,并且您不尝试将它们传递给函数。这样做是有点棘手 https://stackoverflow.com/q/4051/1593077因为最外层的维度经历衰减,所以你实际上会传递一个指针。但是使用 mdspans,你可以这样写:

    template <typename T, typename Extents>
    void print_3d_array(std::mdspan<T, Extents> ms3)
    {
       static_assert(ms3.rank() == 3, "Unsupported rank");
       // read back using 3D view
       for(size_t i=0; i != ms3.extent(0); i++) {
         fmt::print("slice @ i = {}\n", i);
         for(size_t j=0; j != ms3.extent(1); j++) {
           for(size_t k=0; k != ms3.extent(2); k++)
             fmt::print("{} ",  ms3[i, j, k]);
           fmt::print("\n");
         }
       }  
    }
    
    int main() {
        int arr[2][3][2] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12 };
    
        auto ms3 = std::mdspan(&arr[0][0][0], 2, 3, 2);
          // Note: This construction can probably be improved, it's kind of fugly
    
        print_3d_array(ms3);
    }
    

标准化状况

While std::span在 C++20 中标准化,std::mdspan不是。然而,它是 C++23 的一部分,几乎已经完成(等待最终投票)。

您已经可以使用参考实现 https://github.com/kokkos/mdspan。它是美国桑迪亚国家实验室的一部分“Kokkos 性能便携生态系统” https://kokkos.github.io/.

“那些‘额外能力’是什么?mdspan优惠吗?”

An mdspan实际上有 4 个模板参数,不仅仅是元素类型和范围:

template <
    class T,
    class Extents,
    class LayoutPolicy = layout_right,
    class AccessorPolicy = default_accessor<ElementType>
>
class mdspan;

这个答案已经相当长了,所以我们不会给出完整的细节,但是:

  • 某些范围可以是“静态”而不是“动态”,在编译时指定,因此不存储在实例数据成员中。仅存储“动态”实例。例如,这个:

    auto my_extents extents<dynamic_extent, 3, dynamic_extent>{ 2, 4 };
    

    ...是一个范围对象,对应于dextents<size_t>{ 2, 3, 4 },但只存储值2 and 4在类实例中;编译器知道它需要插入3每当使用第二个维度时。

  • 您可以使用 Fortran 风格将维度从小到大,而不是像 C 中那样从大到小。因此,如果您设置LayoutPolicy = layout_left, then mds[x,y] is at mds.data[mds.extent(0) * y + x]而不是通常的mds.data[mds.extent(1) * x + y].

  • 你可以“重塑”你的mdspan进入另一个mdspan尺寸不同但总体尺寸相同。

  • 您可以使用“strides”定义布局策略:让 mdspan 中的连续元素在内存中保持固定距离;有额外的偏移量以及每条线或维度切片的开头和/或结尾; ETC。

  • 你可以“切割”你的mdspan每个维度都有偏移(例如,采用矩阵的子矩阵) - 结果仍然是mdspan! ...那是因为你可以拥有mdspan with a LayoutPolicy其中包含了这些偏移量。此功能在 C++23 IIANM 中不可用。

  • 使用AccessorPolicy, 你(们)能做到mdspan实际上是do单独或集体拥有他们引用的数据。

进一步阅读

  • 官方std::mdspan提议 https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2022/p0009r18.html,被 C++23 接受。
  • The std::mdspan page https://en.cppreference.com/w/cpp/container/mdspan on cppreference.com http://cppreference.com/
  • mdspan 的简单介绍 https://github.com/kokkos/mdspan/wiki/A-Gentle-Introduction-to-mdspan,在Kokkos 参考实现 https://github.com/kokkos/mdspan's wiki.
  • 一看mdspan https://www.ashermancinelli.com/std-mdspan-tensors的,作者:Asher Macinelli。

(some examples were adapted from these sources.)

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

什么是 mdspan,它的用途是什么? 的相关文章

随机推荐

  • 如何使用curl 通过代理模拟来自Firefox 的请求?

    特别是 当我将 Firefox 设置为使用 https 流量代理时 我尝试复制 Firefox 的行为 根据我的测试 Firefox 似乎向代理发送 CONNECT 请求 而以下curl 命令直接向代理发送 GET curl proxy b
  • 将列表框的选定值显示为标签 - 多个值

    我有一个名为 lstPTLNameDHOD 的列表框 其中有多个 PTL 名称 可以使用 Ctrl 键选择这些名称 我想在标签中显示选定的姓名 或者以某种方式显示提交表单的人可以看到他们提交表单的确切对象 我的问题是我只能在标签上显示一个名
  • Python:使用另一个大字典更新一个大字典

    我正在尝试使用另一个字典中的值来更新大型字典的某些值 其中它们具有相似的键 相同的日期但格式不同 我当前使用的流程太慢 我想减少瓶颈 这是我当前的解决方案 它将更新的字典写入文件 from dateutil import parser Fi
  • 在 Node.js 中共享对象并避免全局变量

    在下面的代码片段中共享数据库连接的最合适的方式是什么 db变量 与我的路由器 控制器 无需转动db变量变成全局变量 var mongo require mongoskin db mongo db config db adress app u
  • 使用 Akka 以编程方式获取临时端口

    如果我在 Akka 中将 ActorSystem 配置为使用临时 tcp 端口 更具体地说 我将 http 端口配置为 0 有没有办法在 ActorSystem 启动后以编程方式获取此端口 任何使用 actorOf 创建 Actor 的尝试
  • 如何找到 3 个数组的交集,同时忽略空数组?

    我必须找到 3 个整数数组的交集元素 有条件地说a b c 如果数组中的任何一个为 null 只需忽略该数组并找到剩余数组的交集 如果三个都为 null 则返回 提前致谢 ps 红宝石1 9 3 一种方法是这样的 a b c tap a a
  • Docker 编写安装requirements.txt

    在我的 docker 镜像中 我正在克隆 git master 分支来检索代码 我使用 docker compose 作为开发环境 并使用卷运行容器 我在从 python requests txt 文件安装新项目需求时遇到了问题 在开发环境
  • 如何在 IntelliJ 中调试 Clojure 文件?

    第5行不能设置断点 其中包含 x IntelliJ 不会让我这样做 我使用了不同的插件 例如拉克洛胡尔 https github com JetBrains la clojure and Cursive https cursivecloju
  • RMarkdown 可折叠面板

    当我正在为学生准备教程时 我需要一种方法来隐藏可折叠面板中的内容 这些内容可以通过单击按钮来显示 我已经使用下面的代码让它工作了 RMarkdown 文件如下所示 title Collapsible Panel output html do
  • 显示文本区域的当前行号和列号

    我正在我的网络应用程序中制作一个文件编辑界面 我有一个包含文件内容的文本区域 当文本区域聚焦时 我想输出光标的位置 即行号和列 这很有用 因为错误消息通常会产生行号 问题是 如何确定文本区域中光标的位置 我正在使用原型库 也许已经有解决方案
  • 使用 Gold Parser 解析项目和包文件 --“IdList”需要帮助

    我正在涉足对象帕斯卡引擎 http goldparser org engine 1 pascal index htm 作者 Rob van den Brink 并且看起来 除了一些小的且容易纠正的错误 它适用于 Delphi 单元文件 但是
  • 验证驾驶执照号码?

    我正在开发 ACH 支付处理器 想知道是否可以根据某些规则来确定驾驶执照领域的范围 对此有什么想法吗 我可以只假设数字还是其他更像 SSN 的数字 Thanks 我为我正在从事的一个项目想出了这个 function utilities fu
  • SQL 查询从列中提取文本并将其存储到同一记录中的不同列

    我需要一些有关 SQL 查询的帮助 我有一个 SQL 表 其中包含已提交表单的列详细信息 我需要获取存储在该列中的部分文本 并将其放入同一行的不同列中 我需要复制的文本位始终位于列中的相同位置 任何帮助将不胜感激 伙计们 我的脑子一片空白
  • Symfony 中的 Doctrine 实体中的 @var 注释是什么?

    也许是一个愚蠢的问题 但我不知道这让我烦恼 当我创建一个实体时app console doctrine generate entity它添加了一个 var每个属性的注释 什么是 var用于 它显然表明了数据类型 但我没有看到任何文档中提到它
  • 无法使用微软编译器编译Qt

    我想使用 VS2010 编译器构建 Qt 4 7 3 但在配置时遇到问题 我正在使用 VS command shell 我的配置命令如下所示 configure exe platform win32 msvc2010 no webkit n
  • 有没有办法在 iOS 中以编程方式打开和关闭蓝牙和/或 WiFi?

    我正在寻找一种简单的方法来在 iOS 4 x 设备 iPhone 和 iPad 上切换蓝牙和 WiFi 的打开和关闭状态 当我在不同的位置和使用场景之间移动时 我会不断切换这些功能 现在需要多次点击并访问 设置 应用程序 我希望创建一个简单
  • 如何使用Python找到any()中匹配的内容?

    我正在用Python工作 使用any https docs python org 2 library functions html any像这样寻找 a 之间的匹配String 数组和从 Reddit API 中提取的评论 目前 我正在这样
  • 保持 GH 主页与 README.md 同步

    在为我的项目创建 GitHub Pages 时 建议我导入现有的README md作为项目的主页 后来我合并了gh pages with master并最终得到了两者index html and README md 问题 是更新README
  • 隐藏链接到 .so 文件的第 3 方 .a 文件中的符号

    我正在构建一个共享 so 库 该库由多个 a 文件和调用它们的瘦 API 层组成 我只希望我的 API 和外部依赖项可见 因此我使用 GCC 提供的 隐藏 可见性构建代码 fvisibility hidden 然而 其中一个库是专有的第三方
  • 什么是 mdspan,它的用途是什么?

    在过去的一年左右的时间里 我注意到 StackOverflow 上有一些与 C 相关的答案 请参考mdspan s 但我从未在 C 代码中真正见过这些 我尝试在我的 C 编译器的标准库目录和C 编码指南 http isocpp github