【问题标题】:C# array question (split)C#数组题(拆分)
【发布时间】:2011-03-20 17:14:27
【问题描述】:

问题很简单——比如说,我有一个函数,它接收数组作为它的参数

void calc(double[] data)

如何将这些数据“拆分”在两个子数组中并传递给这样的子函数

calc_sub(data(0, length/2));
cals_sub(data(length /2, length /2));

我希望你明白了——我会用 C++ 写这个

void calc(double * data, int len)
{
   calc_sub(data, len / 2); //this one modifies data!!
   calc_sub(data + len / 2, len / 2); //this one modifies data too!!
}

如何在没有不必要的内存复制的情况下在 C# 中做同样的事情? 我在这里需要 2 个内存副本。 1)从数据到拆分数据 2) calc_sub 3)从拆分数据返回数据!这是对时间和内存的巨大浪费!

【问题讨论】:

    标签: c# arrays memory copy


    【解决方案1】:

    最简单的可能是使用 LINQ TakeSkip 扩展方法:

    int half = data.Length / 2;
    double[] sub1 = data.Take(half).ToArray();
    double[] sub2 = data.Skip(half).ToArray();
    

    【讨论】:

    • 易于阅读的解决方案,但我很确定它相当慢,因为 tak、skip 和 ToArray 都可能导致集合的简单迭代,从而导致原始数据的两次完整迭代。如果原始数据的大小很小或时间不是问题,这是一个很好的可读解决方案
    • 快吗? CLR 知道,如何优化这玩意?
    • 我总是尝试编写可读代码,然后执行负载测试以验证性能是否没有受到影响。如果是这种情况,请重构。
    • 为了澄清性能:TakeSkip 从原始集合创建一个新的枚举器。因此,在您致电 ToArray 之前,什么都不会发生。一旦你调用ToArray,原始数组的一半将被迭代以创建一个新数组,因此这两个操作基本上都会花费你对原始数组的完整迭代加上存储新子数组所需的内存。
    【解决方案2】:

    简短的回答是,要获得一个子数组,您必须创建一个新数组并复制元素……或者,如果您使用的是 c++,那么您就是 memcpying。

    现在,为什么不采用偏移/计数方法呢? 调用calc_sub(blargh[] array)时,改用calc_sub(blargh[] array, int offset, int count)

    这基本上是 c# 传递指向 start/half-pos 元素的指针并告诉函数只处理数组元素的一半的方法。


    我应该注意,除非您使用的是巨大的双数组,否则您真的不应该担心这一点。 64 位 = 8 个字节。即使你有一个包含 1000 个元素的数组,也就是 8000 字节,大约 8 kB 的内存,你几乎可以在不到一秒的时间内释放出来......

    现在,尽可能节省内存总是一个好主意,但我认为这是过早的优化。

    我还应该注意,数组是通过引用传递的,因为它们是真正的对象,而不是整数或字符串,所以这种方法在内存方面是最好的[它不复制数组;它提供了类似指向数组对象的指针],但它的局限性在于修改函数中的数组将修改在函数调用之外传递的数组。

    【讨论】:

    • 大量使用“那个”方法的代码。复制总是有效的,但没有任何“更智能”的解决方案吗?
    【解决方案3】:

    根据 calc_sub 的作用,您可以创建一个 IEnumerable 类,该类接受数组并迭代数组的某些部分。类似于ArraySegment,但better

    【讨论】:

      【解决方案4】:

      继续编写您自己的子数组函数,如下所示:

      public static class ArrayExtensions
      {
        T[] SubArray<T>(this T[] arr, int startIndex,int count)
        {
          var sub = new T[count];
          Array.Copy(arr,startIndex,sub,o,count);
          return sub;
        }
      }
      

      然后你可以像这样使用它:

      void calc(double[] data)
      {
        var half = data.Length /2;
        data.SubArray(0, half ));
        data.SubArray(half , half ));
      }
      

      【讨论】:

      • 已经在我的public static class Extentions... 我认为必须有。
      • @0xDEAD BEEF,但是 calc sub 返回一个由传递的数组计算的值,对吧?老实说,我们不知道 calc_sub 是做什么的,因此我们不可能 100% 针对您提供建议;我们正在猜测您的情况。
      • @0xDEAD BEEF 拆分数组然后迭代进行计算有什么问题?还是您更愿意进行转​​换操作?
      【解决方案5】:

      如果您可以使calc_sub 方法采用IEnumerable&lt;double&gt; 而不是double[],则可以使用扩展方法创建返回部分数组的表达式:

      void calc(double[] data) {
        int half = data.Length / 2;
        calc_sub(data.Take((half));
        calc_sub(data.Skip(half));
      }
      

      这样您不必将数据复制到新数组中,表达式将从原始数组返回项目。

      【讨论】:

      • 快吗?调用 IEnumberable.value 而不是直接数组访问??
      • 它相当快,但不如直接访问快,但您知道您有性能问题吗?
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2021-08-14
      • 1970-01-01
      • 2021-08-03
      相关资源
      最近更新 更多