将一系列浮点数元素化为一系列结构而不复制?

时间:2014-12-03 04:47:22

标签: c# arrays performance marshalling

假设我有以下代码

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;
    }
}

我想将float []转换为Vec3 []。

有没有办法用Marshaling执行此操作,以便所需的复制比迭代float []并将每个浮点数复制到Vec3 []更快?假设float []非常大。

1 个答案:

答案 0 :(得分:1)

您可以使用System.Runtime.InteropServices.Marshal类来执行此操作:

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

尽管如此,我认为如果可能的话,最好避免使用unsafe,即使有一些可衡量的性能差异。对于我来说,选择上述内容直接管理副本(在发布版本中可能非常快,但可能没有上述速度快),这必须是一个相当显着的性能提升。

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


编辑:出于好奇,我继续做了一个简单的测试。我创建了一个float[] 1MB的大小并复制到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次复印试用unsafe约需1.5秒,安全时间约为3秒。 2倍的加速听起来相当不错,但当然,如果您的程序没有其他任何操作,那么您只能得到它。在一个真实世界的程序中,可能会转化为5%或更低的改进,具体取决于正在发生的其他事情。

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

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