将浮点数数组封送到结构数组而不复制

本文关键字:数组 结构 复制 浮点数 | 更新日期: 2023-09-27 18:32:50

假设我有以下代码

public static class Main
{
    public struct Vec3
    {
        public float x, y, z;
    }
    public void Entry()
    {
        float[9] floats = new floats[9] { 1f, 2f, 3f, 4f, 5f, 6f, 7f, 8f, 9f };
        Vec3[3] vecs;
    }
}

我想将浮点数[]转换为Vec3[]。

有没有办法通过封送处理来做到这一点,这样所需的复制比迭代 float[] 并将每个浮点数复制到 Vec3[] 更快?假设 float[] 非常大。

将浮点数数组封送到结构数组而不复制

您可以使用

System.Runtime.InteropServices.Marshal类来执行此操作:

unsafe
{
    fixed (Vec3* vecsPointer = vecs)
    {
        Marshal.Copy(floats, 0, new IntPtr(vecsPointer), floats.Length);
    }
}

也就是说,我认为如果可能的话,通常最好避免unsafe,即使存在一些可衡量的性能差异。对我来说,选择上述副本而不是直接托管副本(在发布版本中可能非常快,尽管可能不如上述速度快(必须是一个相当显着的性能改进。

当然,您应该仔细衡量每种方法的整体影响,并确保在采用unsafe方法之前值得潜在的维护麻烦。


编辑:出于好奇,我继续做了一个简单的测试。我创建了一个大小为 1MB 的float[]并复制到Vec3[].我使用了上面的内容,并将其与一个简单的循环进行了比较,如下所示:

for (int k = 0, l = 0; k + 2 < floats.Length; k += 3, l++)
{
    vecs[l].x = floats[k];
    vecs[l].y = floats[k + 1];
    vecs[l].z = floats[k + 2];
}

在我的测试中,我每次试用每个副本执行 10,000 次,副本的总大小约为 10GB。

unsafe版本"仅"比安全版本快约(仅慢于(2倍。每10,000份试用大约需要1.5秒unsafe,安全需要3秒。2 倍的加速听起来不错,但当然,只有当你的程序不做其他事情时,你才能得到它。在现实世界的计划中,这可能转化为 5% 或更少的改进,具体取决于其他情况。

我看到的其他一些有趣的事情:

  • 使用 Marshal.Copy((,复制 2 的精确幂比其他东西要快得多。 例如,2^20 字节与其他字节。在这种情况下,它接近 3 倍的加速。
  • 与上述相关,Marshal.Copy(( 似乎对副本的确切大小更加敏感。使用安全代码可以获得更一致的性能,当然速度较慢。
  • 在足够小的缓冲区大小(似乎大约 100-200 字节(下,unsafe代码的设置开销会阻碍它,并且它与安全版本一样慢。