C#大量项目字典

C#大量项目字典,c#,dictionary,C#,Dictionary,我想了解在C#内存中存储大量项目的成本。我需要使用的数据结构是字典或类似的。假设我想要的物品数量大约为1亿件,但应用程序不会立即达到这个数量。我们需要很长时间才能达到限额 我担心摊销后的运营成本,但在任何时候我都承受不起太高的成本。因此,通常对于动态数据结构,当结构已满时,它会重新分配自身。就字典而言,我认为它甚至会对每一项重新编制索引。假设应用程序维护了2000万个条目,这些条目刚刚达到字典的容量。然后,当分配一个新的字典存储时,需要对这2000万个条目重新编制索引 这就是为什么我认为一系列字

我想了解在C#内存中存储大量项目的成本。我需要使用的数据结构是字典或类似的。假设我想要的物品数量大约为1亿件,但应用程序不会立即达到这个数量。我们需要很长时间才能达到限额

我担心摊销后的运营成本,但在任何时候我都承受不起太高的成本。因此,通常对于动态数据结构,当结构已满时,它会重新分配自身。就字典而言,我认为它甚至会对每一项重新编制索引。假设应用程序维护了2000万个条目,这些条目刚刚达到字典的容量。然后,当分配一个新的字典存储时,需要对这2000万个条目重新编制索引

这就是为什么我认为一系列字典可能是个好主意。假设我创建了256个字典,这会立即将每个内部字典的大小限制在100万项以下,这应该是可以管理的,可以动态地建立索引,所有索引都会发生在100万项以下。这样做的代价似乎只是在每次操作中额外进行一次索引,以找到正确的字典进行查找

这是否合理的做法?我的分析是正确的还是C#字典会因为某种原因表现得更好?还有其他更好的解决方案吗?我正在寻找一种与C#dictionary具有相同时间复杂性的数据结构

编辑:dictionary键是一个随机值,因此我只需咬一口就可以在256个字典的数组中找到我的索引,而且非常便宜

我目前不考虑建立数据库,因为我希望所有的项目都能立即提供,而且成本很低。我确实需要用很少的开销在固定的时间内查找。我可以让插入速度变慢,但时间不变。与delete相同,可以稍微慢一点,但需要恒定的时间


应该可以将所有项目放入内存中。这些项目很小,每个项目大约有50字节的数据。因此,数据结构中的每一项都不能有太大的开销。

更新:自从我发布后,我已经编辑了它:

  • 每次传递时存储一个固定大小的对象(字节[50]
  • 在添加到字典之前预先分配所有这些(而不是在循环中创建对象)
  • 在预分配内容后运行GC.Collect()
  • gcAllowVeryLargeObjects
    设置为true
  • 确实是为x64设置的(之前是这样,但后来我切换到“Release”来构建并在外部运行VS.。然后它重置了,所以很糟糕。)
  • 尝试使用和不使用预先分配字典大小
下面是代码:

var arrays = new byte[100000000][];
System.Diagnostics.Stopwatch stopwatch = new System.Diagnostics.Stopwatch();
stopwatch.Start();
for (var i = 0; i<100000000; i++)
{
    arrays[i] = new byte[50];
}
stopwatch.Stop();
Console.WriteLine($"initially allocating arrays took {stopwatch.ElapsedMilliseconds} ms");
stopwatch.Restart();

GC.Collect();
Console.WriteLine($"GC after array allocation took {stopwatch.ElapsedMilliseconds} ms");

Dictionary<int, byte[]> dict = new Dictionary<int, byte[]>(100000000);
//Dictionary<int, byte[]> dict = new Dictionary<int, byte[]>();

for (var c = 0; c < 100; c++)
{
    stopwatch.Restart();
    for (var i = 0; i < 1000000; i++)
    {
        var thing = new AThing();
        dict.Add((c * 1000000) + i, arrays[(c*100000)+i]);
    }
    stopwatch.Stop();
    Console.WriteLine($"pass number {c} took {stopwatch.ElapsedMilliseconds} milliseconds");
}

Console.ReadLine();
你可以清楚地看到它必须重新分配的点。我只是通过将列表的大小增加一倍并复制当前的列表项来猜测,因为在最后有一段很长的时间它没有这样做。但是其中一些非常昂贵(30多秒!哎哟)

如果我预先分配字典大小,则输出如下:

initially allocating arrays took 15494 ms
GC after array allocation took 2622 ms
pass number 0 took 9585 milliseconds
pass number 1 took 107 milliseconds
pass number 2 took 91 milliseconds
pass number 3 took 145 milliseconds
pass number 4 took 83 milliseconds
pass number 5 took 118 milliseconds
pass number 6 took 133 milliseconds
pass number 7 took 126 milliseconds
pass number 8 took 65 milliseconds
pass number 9 took 52 milliseconds
pass number 10 took 42 milliseconds
pass number 11 took 34 milliseconds
pass number 12 took 45 milliseconds
pass number 13 took 48 milliseconds
pass number 14 took 46 milliseconds
..SNIP lots between 30-80ms...
pass number 45 took 80 milliseconds
pass number 46 took 65 milliseconds
pass number 47 took 64 milliseconds
pass number 48 took 65 milliseconds
pass number 49 took 122 milliseconds
pass number 50 took 103 milliseconds
pass number 51 took 45 milliseconds
pass number 52 took 77 milliseconds
pass number 53 took 64 milliseconds
pass number 54 took 96 milliseconds
…在30-80毫秒之间剪下批次。。。 77号通行证耗时44毫秒 78号通行证耗时85毫秒 79号通行证耗时142毫秒 80号通行证耗时138毫秒 81号通行证耗时47毫秒 密码82用了44毫秒 …在30-80毫秒之间剪下批次。。。 93号通行证耗时52毫秒 94号通行证耗时50毫秒 95号通行证耗时63毫秒 96号通行证耗时111毫秒 97号通行证耗时175毫秒 98号通行证耗时96毫秒 99号通行证耗时67毫秒

在最初创建阵列时,内存使用量上升到略高于9GB,在GC.Collect之后下降到约6.5GB,在添加到字典时,内存使用量上升到超过9GB,然后在所有操作完成后(它坐在控制台上等待.Readline()),稍过一会儿,内存使用量下降到约3.7GB并保持不变

不过,预先分配字典显然要快得多

***以下原件仅供参考****

我刚刚编写了这个小测试。我不知道你在存储什么,所以我刚刚创建了一个没有太多信息的无意义的小类,并使用int作为键,但我从中得到的两个收获是:

  • 添加到字典的速度似乎不会越来越慢,直到它达到大约4000万个条目。运行针对x64的“发布”版本时,每插入100万个条目大约需要500毫秒,然后从41到46个条目大约需要700-850毫秒(在这一点上可以看到跳跃)

  • 它到达了超过46000000个条目,消耗了大约4GB的RAM,并且由于内存不足而崩溃

  • 使用数据库,否则字典滥用小组会来把你搞垮

  • 代码:

    class-AThing
    {
    公共字符串名称{get;set;}
    公共int id{get;set;}
    }
    班级计划
    {
    静态void Main(字符串[]参数)
    {
    Dictionary dict=新字典();
    对于(var c=0;c<100;c++)
    {
    DateTime nowTime=DateTime.Now;
    对于(变量i=0;i<1000000;i++)
    {
    var thing=newathing{id=(c*1000000)+i,Name=$“Item{(c*1000000)+i}”;
    dict.Add(thing.id,thing);
    }
    var timetake=DateTime.Now-nowTime;
    WriteLine($“passnumber{c}花费了{timetake.millides}毫秒”);
    }
    }
    }
    
    如果希望程序在字典处于最大大小时工作,那么为什么不从一开始就将其分配到最大大小,并避免完全重新索引等。使用的内存量只是暂时不同于其他解决方案,但节省的时间不是暂时的,而且,当字典
    initially allocating arrays took 15494 ms
    GC after array allocation took 2622 ms
    pass number 0 took 9585 milliseconds
    pass number 1 took 107 milliseconds
    pass number 2 took 91 milliseconds
    pass number 3 took 145 milliseconds
    pass number 4 took 83 milliseconds
    pass number 5 took 118 milliseconds
    pass number 6 took 133 milliseconds
    pass number 7 took 126 milliseconds
    pass number 8 took 65 milliseconds
    pass number 9 took 52 milliseconds
    pass number 10 took 42 milliseconds
    pass number 11 took 34 milliseconds
    pass number 12 took 45 milliseconds
    pass number 13 took 48 milliseconds
    pass number 14 took 46 milliseconds
    ..SNIP lots between 30-80ms...
    pass number 45 took 80 milliseconds
    pass number 46 took 65 milliseconds
    pass number 47 took 64 milliseconds
    pass number 48 took 65 milliseconds
    pass number 49 took 122 milliseconds
    pass number 50 took 103 milliseconds
    pass number 51 took 45 milliseconds
    pass number 52 took 77 milliseconds
    pass number 53 took 64 milliseconds
    pass number 54 took 96 milliseconds
    
    class AThing
    {
        public string Name { get; set; }
        public int id { get; set; }
    }
    class Program
    {
        static void Main(string[] args)
        {
            Dictionary<int, AThing> dict = new Dictionary<int, AThing>();
    
            for (var c = 0; c < 100; c++)
            {
                DateTime nowTime = DateTime.Now;
                for (var i = 0; i < 1000000; i++)
                {
                    var thing = new AThing { id = (c * 1000000) + i, Name = $"Item {(c * 1000000) + i}" };
                    dict.Add(thing.id, thing);
                }
                var timeTaken = DateTime.Now - nowTime;
                Console.WriteLine($"pass number {c} took {timeTaken.Milliseconds} milliseconds");
            }
    
        }
    }