【问题标题】:Convert an array of doubles to an array of structs with only double members without copying data将双精度数组转换为仅具有双精度成员而不复制数据的结构数组
【发布时间】:2018-07-16 07:25:10
【问题描述】:

我正在使用第三方 C++ 库在 Julia 中完成一些繁重的工作。在 Julia 方面,数据存储在 Array{Float64, 2} 类型的对象中(这大致类似于二维双精度数组)。我可以使用指向double 的指针将它传递给C++。然而,在 C++ 端,数据存储在一个名为 vector3 的结构中:

typedef struct _vector3
{
    double x, y, z;
} vector3;

我的快速而肮脏的方法是一个五步过程:

  1. 在 C++ 端动态分配结构数组
  2. 将输入数据从double*复制到vector3*
  3. 做繁重的工作
  4. 将输出数据从vector3*复制到double*
  5. 删除动态分配的数组

复制大量数据是非常低效的。我可以使用一些神秘的技巧来避免将数据从double 复制到struct 并返回吗?我想以某种方式将 double 的一维数组(大小为 3 的倍数)解释为具有 3 个 double 成员的结构的一维数组。

【问题讨论】:

  • 我敢打赌,复制 3 个 64 位值会对计算产生边际影响(假设计算并不比仅添加复杂)
  • @PeterJ_01 我总是在复制结构数组,因此我复制了 N * 3 * 64 位,其中 N 可以大于一百万。
  • 和他们你用一个做多少操作?如果不是太多 - 在朱莉娅做。
  • 之所以使用第三方库是因为在Julia中我有一个O(N^2)复杂度的算法,而第三方库有一个O(N)复杂度的算法。 O(N)算法很复杂,我不打算自己实现。

标签: c++ struct casting julia


【解决方案1】:

很遗憾,你不能。这是因为 C++ 有 aliasing rule。简而言之,如果你有一个对象T,你就不能从不兼容类型的指针U 合法地访问它。从这个意义上说,您不能通过struct _vector3 类型的指针访问double 或double* 类型的对象,反之亦然。

如果你挖得够深,你会发现reinterpret_cast 并且可能会想“哦,这正是我需要的”,但事实并非如此。不管你用什么诡计(reinterpret_cast 或其他方式)绕过语言限制(也称为让它编译),事实仍然是你只能合法地访问double 类型的对象通过double 类型的指针。

一个经常用于键入 pune 的技巧是使用union。在C 中是合法的,但在 C++ 中是非法的,但一些编译器允许这样做。但是,在您的情况下,我认为没有办法使用联合。

理想的情况是直接对double* 数据进行繁重的工作。如果这对您的工作流程可行。

【讨论】:

    【解决方案2】:

    严格来说,你不能。前段时间我问过一个类似的问题 (Aliasing struct and array the C++ way),答案解释了为什么直接别名会调用未定义行为,并就可能的解决方法给出了一些提示。

    话虽如此,您已经处于极端情况,因为原始数据来自不同的语言。这意味着 C++ 标准未涵盖该数据的处理,仅由您正在使用的实现定义(gcc/version 或 clang/version 或...)

    对于您的实现,将外部数组别名为 C++ 结构或将外部结构别名为 C++ 数组可能是合法的。您应仔细阅读混合语言编程的文档,以便准确实施。

    【讨论】:

    • 还要注意可能的结构填充
    【解决方案3】:

    其他答案提到了实际问题(转换可能无法正常工作)。我将添加一个小的运行时检查来验证别名是否有效,并提供一个占位符,您可以在其中调用/使用您的大量复制代码。

    int aliasing_supported_internal() {
        double testvec[6];
        _vector3* testptr = (_vector3*)(void*)testvec;
        // check that the pointer wasn't changed
        if (testvec != (void*)testptr) return 0;
        // check for structure padding
        if (testvec+3 != (void*)(testptr+1)) return 0;
        // TODO other checks?
        return 1;
    }
    int aliasing_supported() {
        static int cached_result = aliasing_supported_internal();
        return cached_result;
    }
    

    此代码将一个小的双精度数组转换为一个结构数组别名(不是复制),然后检查它是否有效。如果转换成功(函数返回 1),您很可能可以自己使用相同类型的别名(通过 void 指针进行转换)。

    请注意,代码仍可能以意想不到的方式被破坏。严格的别名规则规定,即使是上述检查也是未定义的行为。这可能会奏效,也可能会严重失败。只有允许正常工作的转换是 void* 并返回到原始指针类型。此外,上述检查在多重继承层次结构或虚拟基类中可能完全错误(从某种意义上说,两者转换为 void* 都是不安全的,因为实际指针值可能会移动,也就是说,可能会由于对齐以外的约束而改变和相当多的字节)

    【讨论】:

      猜你喜欢
      • 2015-09-13
      • 2011-07-07
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多