【问题标题】:Pure Speed for Lookup Single Value Type c#?查找单值类型c#的纯速度?
【发布时间】:2014-05-30 16:13:47
【问题描述】:

.NET 4.5.1

我有一组 Int16 值,它们的范围从 -4 到 32760。范围内的数字不是连续的,但它们的顺序是从 -4 到 32760。换句话说,从 16 开始的数字-302 不在“一堆”中,但数字 303-400 在那里,数字 2102 不在,等等。

确定某个特定值(例如 18400)是否在“组”中的最快方法是什么?现在它在一个 Int16[] 中,并且 Linq Contains 方法用于确定一个值是否在数组中,但是如果有人能说出为什么/如何不同的结构可以更快地提供单个值,我将不胜感激。速度是这种查找的关键(“bunch”是静态类的静态属性)。

有效的示例代码

Int16[] someShorts = new[] { (short)4 ,(short) 5 , (short)6};
var isInIt = someShorts.Contains( (short)4 );

我不确定这是否是可以做到的最高效的事情。

谢谢。

【问题讨论】:

  • 数组排序了吗?这些数字是唯一的吗,或者例如你会有两次 18400 吗?
  • 什么@lc。问;您似乎暗示它们是有序且独特的,但我也不是 100% 的。
  • @lc 没有重复的数字,每个数字都出现在“一堆”中一次。这些数字按从最小 (-4) 到最大 (32760) 的数字顺序排列。

标签: c# arrays performance linq


【解决方案1】:

听起来你真的想要BitArray - 只需将值偏移 4,这样你就有了[0, 32764] 的范围,你应该没问题。

这将分配一个有效大小为 4K (32764 / 8) 的数组,数组中每个值一个位。它将处理在数组中查找相关元素,并应用位掩码。 (我不知道它是在内部使用byte[] 还是别的什么。)

与存储范围相比,这可能是一种不太紧凑的表示,但获取/设置位所涉及的唯一成本将是计算索引(基本上是移位),获取相关的内存位CPU,然后位掩码。它的大小是bool[] 的 1/8,使您的 CPU 缓存使用效率更高。

当然,如果这确实是您的性能瓶颈,您应该在您的实际应用程序中比较此解决方案和 bool[] 方法 - 在这里,微基准并不像您的实际应用程序的行为那么重要。

【讨论】:

    【解决方案2】:

    为每个可能的值创建一个布尔值:

    var isPresentItems = new bool[32760-(-4)+1];
    

    如果给定的项目存在于集合中,则将相应的元素设置为true。查找很简单:

    var isPresent = isPresentItems[myIndex];
    

    不能再快了。布尔值将适合 L1 或 L2 缓存。

    我建议不要使用BitArray,因为它每个字节存储多个值。这意味着每次访问都比较慢。需要位算术。

    如果您想要超快的速度,请不要让 LINQ 为每个项目调用一次委托。 LINQ 不是性能关键代码的首选。许多间接导致 CPU 停滞。

    【讨论】:

    • 虽然 BitArray 需要位运算,但它仍会消耗 1/8 的内存 - 允许 其他 事物也位于 L1/L2 缓存中。
    • @JonSkeet 有效点。我们不知道哪种变体更好。取决于应用程序的其余部分。
    • 确实 - 我在回答中对此进行了扩展。
    【解决方案3】:

    如果您想优化查找时间,请选择具有O(1)(恒定时间)查找的数据结构。您有多种选择,因为您只关心集合成员,而不关心排序或排序。

    HashSet<Int16> 将把这个给你,BitArraymax - min + 1 上的索引也会给你。正如@usr 所建议的那样,绝对最快的临时解决方案可能是一个在max - min + 1 上索引的简单数组。这些中的任何一个都应该足够“足够快”。 HashSet<Int16> 可能会使用最多的内存,因为内部哈希表的大小是一个实现细节。 BitArray 将是这些选项中最节省空间的。

    如果您只有一次查找,那么内存应该不是问题,我建议首先使用HashSet<Int16>。该解决方案很容易以无错误的方式进行推理和处理,因为您不必担心停留在数组边界内;你可以简单地检查set.Contains(n)。如果您的价值范围将来可能发生变化,这将特别有用。如果您需要进一步优化速度或性能,您可以退回到其他解决方案之一。

    【讨论】:

      【解决方案4】:

      一种选择是使用HashSet。要查找值是否在其中,这是一个 O(1) 操作

      代码示例:

      HashSet<Int16> evenNumbers = new HashSet<Int16>();
      
          for (Int16 i = 0; i < 20; i++)
          {
              evenNumbers.Add(i);
          }
      
      
          if (evenNumbers.Contains(0))
          {
             /////
          }
      

      【讨论】:

        【解决方案5】:

        因为数字是排序的,所以我会循环遍历列表一次并生成一个 Range 对象列表,这些对象有一个开始和结束编号。该列表比拥有数千个数字的列表或字典要小得多。

        【讨论】:

          【解决方案6】:

          如果您的“一堆”数字可以识别为一系列区间,我建议您使用Interval Trees。区间树允许动态插入/删除,并且还可以搜索区间是否与树中的任何区间相交 O(log(n)),其中 n 是树中的 区间数。在您的情况下,间隔的数量将远小于整数的数量,并且搜索速度要快得多。

          【讨论】:

            猜你喜欢
            • 2016-05-31
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 2013-09-14
            • 1970-01-01
            • 1970-01-01
            相关资源
            最近更新 更多