【问题标题】:Efficient list of IP addresses有效的 IP 地址列表
【发布时间】:2013-07-02 20:07:47
【问题描述】:

使用 IP4 地址,显然可以以 32 位存储。 我需要跟踪一个 ip 列表,这可能是一个相当长的列表,因此希望尽可能保持紧凑。我还需要快速搜索列表,以检查其中是否已加载 IP。

我目前正在研究:将 IP 转换为 UInt32,然后将列表存储在 HashSet 中。

我在想可能有更好的方法吗?

更新:哈希集当然会生成哈希,它大于 uint 的 4 个字节。因此,要真正优化这一点,特别是针对 IP4 地址,需要一个类似的结构,针对 4 个字节进行优化

【问题讨论】:

  • 您可以将其存储为 trie:en.wikipedia.org/wiki/Trie
  • 这个有内置结构吗?
  • HashSet 听起来不错。你想改进什么?会不会占用太多空间、太多时间?多线程访问集合时是否存在争用问题?
  • 不是内置的,但我确信网上有通用的
  • I need to keep track of a list of ip's, which can be a fairly long list 多少? 10K? 1M? 10M?即使对于我的旧 32 位 PC,它们都不是很多。

标签: c# .net data-structures hashset


【解决方案1】:

如果列表是相对静态的(即不经常更改),那么数组或List<uint> 将是一种非常简单的存储方式。它为您提供使用 BinarySearch 的 O(log n) 查找,这可能足够快,除非您每秒进行数千次查找。但是,在列表中插入一个新项目是一个 O(n) 操作。如果你必须做很多插入,这不是要走的路。

HashSet<uint> 运行良好,查找和插入速度更快。但这会让你付出代价。 HashSet<uint> 占用的内存大约是 List<uint> 的 3 倍。

3X 内存使用的理由:

下面的程序分配了一个包含 89,478,457 个项目的 List<uint>,这曾经是一个可以创建的最大大小的 HashSet。 (直到 .NET 4.0。)然后它用唯一值填充该列表并从列表中创建一个 HashSet<uint>

程序通过调用GC.GetTotalMemory(true) 计算分配的总内存,这会强制进行垃圾回收。然后它计算列表和散列集所需的内存量。

使用 .NET 4.5、Visual Studio 2012 运行测试。在没有附加调试器的情况下以发布模式运行。

我的输出:

Max size = 89,478,457
Starting memory = 53,240
89,000,000
After list populated = 357,967,136
89,478,457 items in the HashSet
After HashSet populated = 1,789,622,704
List occupies 357,913,896
HashSet occupies 1,431,655,568
HashSet occupies 4.00 times the memory of List
Press Enter:

所以我错了……uint 是 4X。 ulong 是 3.5 倍。

    private void DoStuff()
    {
        int maxSize = 89478457;
            //89000000;
        Console.WriteLine("Max size = {0:N0}", maxSize);

        var startMem = GC.GetTotalMemory(true);
        Console.WriteLine("Starting memory = {0:N0}", startMem);

        // Initialize a List<long> to hold maxSize items
        var l = new List<uint>(maxSize);

        // now add items to the list
        for (uint i = 0; i < maxSize; i++)
        {
            if ((i % 1000000) == 0)
            {
                Console.Write("\r{0:N0}", i);
            }
            l.Add(i);
        }
        Console.WriteLine();

        var memAfterListAlloc = GC.GetTotalMemory(true);
        Console.WriteLine("After list populated = {0:N0}", memAfterListAlloc);

        // Construct a HashSet from that list
        var h = new HashSet<uint>(l);

        Console.WriteLine("{0:N0} items in the HashSet", h.Count);

        var memAfterHashAlloc = GC.GetTotalMemory(true);
        Console.WriteLine("After HashSet populated = {0:N0}", memAfterHashAlloc);

        var listMem = memAfterListAlloc - startMem;
        var hashMem = memAfterHashAlloc - memAfterListAlloc;

        Console.WriteLine("List occupies {0:N0}", listMem);
        Console.WriteLine("HashSet occupies {0:N0}", hashMem);

        Console.WriteLine("HashSet occupies {0:N2} times the memory of List", (double)hashMem / listMem);

        GC.KeepAlive(l);
        GC.KeepAlive(h);

        Console.Write("Press Enter:");
        Console.ReadLine();
    }

【讨论】:

  • 如果你证明 A HashSet&lt;uint&gt; will occupy about 3 times as much memory as a List&lt;uint&gt; 是合理的,我准备撤消我的反对票
  • @I4V:我关于该主题的博文:blog.mischel.com/2008/05/21/more-on-net-collection-sizes。最后一段的旁边有 24 个字节的每项语句。
  • 优秀的答案。我认为哈希会使用额外的内存(因为它创建的哈希值比简单的 4 字节 int 更大),但我也认为列表的大小大致相同。很高兴知道。
猜你喜欢
  • 1970-01-01
  • 2016-12-01
  • 2015-06-26
  • 1970-01-01
  • 2019-03-22
  • 2012-09-17
  • 2020-01-03
  • 2014-08-15
  • 2011-04-09
相关资源
最近更新 更多