我有一个大约有4千万个项目的字典,我试图根据字典中每个keyvaluepair值中定义的ulong来获得一个独特的计数。
我目前正在这样做:
int Total = (from c in Items select c.Value.Requester).Distinct().Count();
唯一的问题是我的应用程序使用了大约3.9GB的ram,而且这个方法似乎正在复制它找到的那些项目(这恰好是字典中约95%的项目)所以在GC开始处理所有内容之前,ram的使用量增加了几千兆字节。
有没有办法在不制作副本的情况下获得明确的计数?
答案 0 :(得分:2)
不,你不能这样做。它需要复制值,因为它需要记住它之前看到的值。
如果您有一个列表,其中的项目按Value.Requester排序,那么您可以使用单个线性扫描计算不同的值而无需复制。但你没有那个。
如果您知道您的值位于特定范围内(例如1到100,000,000),您可以使用位数组编写更高效的内存算法。你可以创建一个100,000,000比特的数组(一个320万字节的数组),它只消耗大约12.5兆字节,并使用它来存储你看到的值。
以下是您可以使用的一些代码:
// Warning: this scans the input multiple times!
// Rewriting the code to only use a single scan is left as an exercise
// for the reader.
public static int DistinctCount(this IEnumerable<int> values)
{
int min = values.Min();
int max = values.Max();
uint[] bitarray = new uint[(max - min + 31) / 32];
foreach (int value in values)
{
int i = (value - min) / 32;
int j = (value - min) % 32;
bitarray[i] |= (uint)(1 << j);
}
uint count = 0;
for (int i = 0; i < bitarray.Length; ++i)
{
uint bits = bitarray[i];
while (bits != 0)
{
count += bits & 1;
bits >>= 1;
}
}
return (int)count;
}
像这样使用:
int Total = (from c in Items select c.Value.Requester).DistinctCount();
答案 1 :(得分:0)
您可能需要重新考虑如何创建字典。如果要从文件构建它,您可能希望一次读取较小的块。要获取不同的项目,您可以从字典文件的每个块开始向HashSet<>
添加项目。 HashSet<>
的最终大小将是不同项目的数量。这种方法可能仍然很慢,因为集合需要做的工作是确保每次向集合添加值时都不存在值。
我会从Mark的答案中得到一些提示:确保在将输入读入应用程序之前对输入进行排序:如果数据已排序,您可以在一次通过中计算不同的项目(基本上计算值的次数) n
的值与n + 1
的值不同。
答案 2 :(得分:0)
正如其他人已经指出的那样,你使用的结构无法复制而无法复制......
如果您确实需要使用当前结构执行此操作,我认为您将不得不引入一些冗余...即,当您从这个“大词典”插入/删除项目时,保留第二个相当小的一个只保持不同的带有计数的值(请注意多线程问题)......
至于替代方案:
使用数据库...如果需要有内存数据库......但我非常确定基于磁盘的数据库不仅仅是完成任务(每小时4000万小时)每秒20K)...我更像是一个甲骨文家伙......但SQLite,Postgres等对此也是绝对正确的......如果你想要的话,可以使用SQLite作为纯粹的“内存数据库” /或者您可以创建一个RAM磁盘并将数据库文件放在那里。
答案 3 :(得分:0)
虽然在大多数情况下它实际上没用,但是在技术上可以使用简单的O(n ^ 2)算法(这需要几分钟才能在40 000 000个项目上执行)
public static int DistinctCount(this IEnumerable<int> values)
{
int max = values.Max();
int last = int.MinValue;
int result = 0;
do
{
int current = int.MaxValue;
foreach (int value in values)
{
if (value < current && value > last)
{
current = value;
}
}
result++;
last = current;
} while (last != max);
return result;
}