jjzjj

c# - 非常大的集合的效率;迭代和排序

coder 2023-07-08 原文

我有一个 csv 解析器,它读取 15+ 百万行(有很多重复项),一旦解析为结构,就需要添加到集合中。每个结构都有属性 Key (int)、A(datetime) 和 B(int)(以及此处不相关的其他属性)。

要求 A:集合需要通过键强制唯一性。

要求 B:在后面的步骤中,我需要按属性 A(时间戳)然后 B(整数)对集合进行排序。

约束结构最终需要按顺序遍历,一个接一个,并引用邻居(LinkedList 在这里提供了最干净的解决方案);此操作的要点是对集合进行分区。请假设这是最早可能发生的分区(即,它不能在解析阶段进行分区)。

我发现 SortedSet 对于要求 A 工作得很好,而且它的性能也相当好,即使 O(log n) 插入比 HashSet<T> 慢得多。的 O(1),尽管我不关心键的排序。 HashSet<T>当集合变大时会陷入困境,这显然是一个已知问题,而 SortedSet<T>没有这个缺点。

问题:当我到达要求 B 的步骤时,对集合进行排序(将 SortedSet<T> 作为 IEnumerable<T> 传递给方法)花费了太多时间(20 多分钟)研磨,全部在内存中,不使用页面文件)。

问题:哪个集合最适合解决这个问题?一个想法是使用两个集合:一个强制执行唯一性(如键的 HashSet<int>SortedSet<int>),第二个 SortedSet<T>在解析阶段处理排序(即,尽可能上游)。但是应用程序已经是内存密集型的,需要页面文件的性能损失是令人望而却步的。
对于按一个特征强制唯一性但按其他不相关特征排序的单个集合,这给我留下了什么选择? SortedSet<T>使用 IComparer<T> (但不是 IComparer<T>IEquitable<T> ),所以如果它依赖 CompareTo 来强制执行唯一性,那么它似乎不符合我的要求。子类化 SortedSet 是可行的方法吗?

编辑:排序代码:

SortedSet<Dto> parsedSet = {stuff};
var sortedLinkedStructs = new LinkedList<Dto>(parsedSet.OrderBy(t => t.Timestamp).ThenBy(i => i.SomeInt));

结构:

public readonly struct Dto: IEquatable<Dto>, IComparer<Dto>, IComparable<Dto>
{
     public readonly datetime Timestamp;
     public readonly int SomeInt;
     public readonly int Key;

     ctor(ts, int, key){assigned}

     public bool Equals(Dtoother) => this.Key == other.Key;
     public override int GetHashCode() => this.Key.GetHashCode();
     public int Compare(Dto x, Dto y) =>  x.Key.CompareTo(y.Key);
     public int CompareTo(Dto other) => this.Key.CompareTo(other.Key);
}

最佳答案

这可能不是一个直接的答案,但是:这是我在类似规模的类似系统中成功使用的一种方式。这是用于在 Stack Overflow 上驱动问题列表的“标签引擎”;本质上,我有一个:

struct Question {
    // basic members - score, dates, id, etc - no text
}

基本上一个超大的Question[](实际上我在非托管内存中使用了一个Question*,但那是因为我需要能够出于不相关的原因与一些 GPU 代码共享它)。填充数据只是取出 Question[] 中的连续行。此数据永远不会排序 - 它作为源数据单独保留 - 只需附加(新键)或覆盖(相同键); 在最坏的情况下,如果达到最大容量,我们可能需要重新分配数据并将数据 block 复制到新阵列。

现在,我没有对数据进行排序,而是单独保留一个int[](实际上是int*,原因与之前相同,但是...嗯),其中 int[] 中的每个值都是 Question[ 中实际数据的索引 ]。所以最初它可能是 0, 1, 2, 3, 4, 5, ...(虽然我预先过滤了这个,所以它只包含我想保留的行 - 删除“删除”等)。

使用 或者 修改器并行快速排序(参见 http://stackoverflow.com/questions/1897458/parallel-sort-algorithm )或修改后的“内省(introspection)排序”(如 here ) - 所以在排序结束时,我可能有 0, 3, 1, 5, ...

现在:为了遍历数据,我只是遍历 int[],并将其用作对 Question 中的实际 数据的查找[]。这最大限度地减少了排序期间的数据移动量,并允许我非常有效地保持多个单独的排序(可能使用不同的预过滤器)。对 15M 数据进行排序仅需几毫秒(每分钟左右发生一次,以便将新问题引入 Stack Overflow,或记录对现有问题的更改)。

为了尽可能快地进行排序,我尝试编写我的排序代码,以便可以用单个整数值表示复合排序,从而实现非常有效的排序(可用于内省(introspection)排序).例如,这里是“最后一个事件日期,然后是问题 ID”排序的代码:

public override bool SupportsNaturallySortableUInt64 => true;
public override unsafe ulong GetNaturallySortableUInt64(Question* question)
{
    // compose the data (MSB) and ID (LSB)
    var val = Promote(question->LastActivityDate) << 32
        | Promote(question->Id);
    return ~val; // the same as ulong.MaxValue - val (which reverses order) but much cheaper
}

它的工作原理是将 LastActivityDate 视为 32 位整数,左移 32 位并将其与作为 32 位整数的 Id 组合,这意味着我们可以在单个操作中比较日期和 ID。

或者对于“分数,然后是答案分数,然后是 id”:

public override unsafe ulong GetNaturallySortableUInt64(Question* question)
{
    // compose the data
    var val = Promote(question->Score) << 48
        | Promote(question->AnswerScore) << 32
        | Promote(question->Id);
    return ~val; // the same as ulong.MaxValue - val (which reverses order) but much cheaper
}

请注意,GetNaturallySortableUInt64 每个元素仅调用一次 - 进入 ulong[] 的工作区域(是的,实际上是 ulong* ) 大小相同,所以最初这两个工作区是这样的:

int[]    ulong[]
0        34243478238974
1        12319388173
2        2349245938453
...      ...

现在我可以通过只查看 int[]ulong[] 来完成整个排序,这样 ulong[] 矢量以排序顺序结束,int[] 包含要查看的项目的索引。

关于c# - 非常大的集合的效率;迭代和排序,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/48345753/

有关c# - 非常大的集合的效率;迭代和排序的更多相关文章

  1. ruby - 为什么 Ruby 的 each 迭代器先执行? - 2

    我在用Ruby执行简单任务时遇到了一件奇怪的事情。我只想用每个方法迭代字母表,但迭代在执行中先进行:alfawit=("a".."z")puts"That'sanalphabet:\n\n#{alfawit.each{|litera|putslitera}}"这段代码的结果是:(缩写)abc⋮xyzThat'sanalphabet:a..z知道为什么它会这样工作或者我做错了什么吗?提前致谢。 最佳答案 因为您的each调用被插入到在固定字符串之前执行的字符串文字中。此外,each返回一个Enumerable,实际上您甚至打印它。试试

  2. c# - 如何在 ruby​​ 中调用 C# dll? - 2

    如何在ruby​​中调用C#dll? 最佳答案 我能想到几种可能性:为您的DLL编写(或找人编写)一个COM包装器,如果它还没有,则使用Ruby的WIN32OLE库来调用它;看看RubyCLR,其中一位作者是JohnLam,他继续在Microsoft从事IronRuby方面的工作。(估计不会再维护了,可能不支持.Net2.0以上的版本);正如其他地方已经提到的,看看使用IronRuby,如果这是您的技术选择。有一个主题是here.请注意,最后一篇文章实际上来自JohnLam(看起来像是2009年3月),他似乎很自在地断言RubyCL

  3. C# 到 Ruby sha1 base64 编码 - 2

    我正在尝试在Ruby中复制Convert.ToBase64String()行为。这是我的C#代码:varsha1=newSHA1CryptoServiceProvider();varpasswordBytes=Encoding.UTF8.GetBytes("password");varpasswordHash=sha1.ComputeHash(passwordBytes);returnConvert.ToBase64String(passwordHash);//returns"W6ph5Mm5Pz8GgiULbPgzG37mj9g="当我在Ruby中尝试同样的事情时,我得到了相同sha

  4. 基于C#实现简易绘图工具【100010177】 - 2

    C#实现简易绘图工具一.引言实验目的:通过制作窗体应用程序(C#画图软件),熟悉基本的窗体设计过程以及控件设计,事件处理等,熟悉使用C#的winform窗体进行绘图的基本步骤,对于面向对象编程有更加深刻的体会.Tutorial任务设计一个具有基本功能的画图软件**·包括简单的新建文件,保存,重新绘图等功能**·实现一些基本图形的绘制,包括铅笔和基本形状等,学习橡皮工具的创建**·设计一个合理舒适的UI界面**注明:你可能需要先了解一些关于winform窗体应用程序绘图的基本知识,以及关于GDI+类和结构的知识二.实验环境Windows系统下的visualstudio2017C#窗体应用程序三.

  5. postman——集合——执行集合——测试脚本——pm对象简单示例02 - 2

    //1.验证返回状态码是否是200pm.test("Statuscodeis200",function(){pm.response.to.have.status(200);});//2.验证返回body内是否含有某个值pm.test("Bodymatchesstring",function(){pm.expect(pm.response.text()).to.include("string_you_want_to_search");});//3.验证某个返回值是否是100pm.test("Yourtestname",function(){varjsonData=pm.response.json

  6. ruby-on-rails - 使用 HTTParty 的非常基本的 Rails 4.1 API 调用 - 2

    Rails相对较新。我正在尝试调用一个API,它应该向我返回一个唯一的URL。我的应用程序中捆绑了HTTParty。我已经创建了一个UniqueNumberController,并且我已经阅读了几个HTTParty指南,直到我想要什么,但也许我只是有点迷路,真的不知道该怎么做。基本上,我需要做的就是调用API,获取它返回的URL,然后将该URL插入到用户的数据库中。谁能给我指出正确的方向或与我分享一些代码? 最佳答案 假设API为JSON格式并返回如下数据:{"url":"http://example.com/unique-url"

  7. ruby-on-rails - 需要帮助最大化多个相似对象中的 3 个因素并适当排序 - 2

    我需要用任何语言编写一个算法,根据3个因素对数组进行排序。我以度假村为例(如Hipmunk)。假设我想去度假。我想要最便宜的地方、最好的评论和最多的景点。但是,显然我找不到在所有3个中都排名第一的方法。Example(assumingthereare20importantattractions):ResortA:$150/night...98/100infavorablereviews...18of20attractionsResortB:$99/night...85/100infavorablereviews...12of20attractionsResortC:$120/night

  8. ruby-on-rails - Ruby .each 效率 - 2

    我这样做(在我看来):#myUserisaUserinActiveRecordwith:has_many:postsmyUser.posts.eachdo|post|end如果用户有10个帖子,这会调用10次数据库吗?这些循环应该像(不那么漂亮)吗?:myPosts=myUser.postsmyPosts.eachdo|post|endHere是我测试的ruby​​文件的粘贴箱。编辑修改了粘贴箱。这让我想起了Java中的代码for(inti=0;i应该是(除非数组被修改)for(inti=0,len=someExpensiveFunction();i我错过了什么吗?我看到一堆Rails

  9. ruby-on-rails - 在具有 ActiveRecord 条件的相关模型中按字段排序 - 2

    我正在尝试按Rails相关模型中的字段进行排序。我研究的所有解决方案都没有解决如果相关模型被另一个参数过滤?元素模型classItem相关模型:classPriority我正在使用where子句检索项目:@items=Item.where('company_id=?andapproved=?',@company.id,true).all我需要按相关表格中的“位置”列进行排序。问题在于,在优先级模型中,一个项目可能会被多家公司列出。因此,这些职位取决于他们拥有的company_id。当我显示项目时,它是针对一个公司的,按公司内的职位排序。完成此任务的正确方法是什么?感谢您的帮助。PS-我

  10. ruby - 按数字(从大到大)然后按字母(字母顺序)对对象集合进行排序 - 2

    我正在构建一个小部件来显示奥运会的奖牌数。我有一个“国家”对象的集合,其中每个对象都有一个“名称”属性,以及奖牌计数的“金”、“银”、“铜”。列表应该排序:1.首先是奖牌总数2.如果奖牌相同,按类型分割(金>银>铜,即2金>1金+1银)3.如果奖牌和类型相同,则按字母顺序子排序我正在用ruby​​做这件事,但我想语言并不重要。我确实找到了一个解决方案,但如果感觉必须有更优雅的方法来实现它。这是我做的:使用加权奖牌总数创建一个虚拟属性。因此,如果他们有2个金牌和1个银牌,加权总数将为“3.020100”。1金1银1铜为“3.010101”由于我们希望将奖牌数排序为最高的,因此列表按降序排

随机推荐