考虑 N 的位 vector 其中的位( N 很大)和 M 的数组数字( M 中等,通常比 N 小得多),每个都在 0..N-1 范围内指示 vector 的哪一位必须设置为 1 .后一个数组未排序。位 vector 只是一个整数数组,特别是 __m256i ,其中每个 __m256i 被打包成 256 位结构体。
如何在多个线程中有效地拆分这项工作?
首选语言是C++(MSVC++2017工具集v141),汇编也很棒。首选 CPU 是 x86_64(内在没问题)。如果有任何好处,则需要 AVX2。
最佳答案
假设您想将这项工作分配给 T线程。这是一个非常有趣的问题,因为它不能通过分区简单地并行化,并且各种解决方案可能适用于不同大小的 N和 M .
完全并发基线
您可以简单地分割数组 M进入 T分区并让每个线程在自己的 M 分区上工作与共享 N .主要问题是因为M未排序,所有线程都可以访问 N 的任何元素因此跺脚彼此的工作。为了避免这种情况,您必须使用诸如 std::atomic::fetch_or 之类的原子操作。对于共享 N 的每次修改数组,或者想出一些锁定方案。这两种方法都可能会降低性能(即,使用原子操作设置位可能比等效的单线程代码慢一个数量级)。
让我们看看可能更快的想法。
私有(private)N
避免需要对 N 的所有突变进行原子操作的“共享 N”问题的一个相对明显的想法是简单地给每个 T 一个 N 的私有(private)拷贝,并在最后通过 or 合并它们。 .
不幸的是,这个解决方案是 O(N) + O(M/T)而原来的单线程解决方案是 O(M)上面的“原子”解决方案类似于 O(M/T) 4.既然我们知道N >> M在这种情况下,这可能是一个糟糕的权衡。不过,值得注意的是,每一项中的隐藏常量非常不同:O(N)来自合并 step0 的术语可以使用 256 位宽 vpor指令,意味着吞吐量接近 200-500 位/周期(如果缓存),而位设置步骤是 O(M/T)我估计接近 1 位/周期。因此,即使 N 的大小,这种方法肯定是中等 T 的最佳方法。是 M 大小的 10 或 100 倍.
M的分区
这里的基本思想是对 M 中的索引进行分区。这样每个工作线程就可以在 N 的不相交部分上工作大批。如 M已排序,那将是微不足道的,但事实并非如此,所以......
一个简单的算法,如果 M 运行良好平滑分布到第一个分区 M 的值进入 T存储桶,其中存储桶的值在 [0, N/T), [N/T, 2N/T], ..., [(T-1)N/T, N) 范围内.即除N进入 T不相交的区域,然后找到 M 的值落入他们每个人。您可以将这项工作传播到 T通过为每个线程分配相等大小的块 M ,并让他们各自创建 T分区,然后在最后将它们逻辑合并 1,这样您就有了 T M 的分区.
第二步是实际设置所有位:为每个线程分配一个分区 T它可以以“单线程”方式设置位,即不用担心并发更新,因为每个线程都在 N 的不相交分区上工作2.
两个步骤 O(M)并且第二步与单线程情况相同,因此并行化的开销是第一步。我怀疑第一个的速度范围从与第二个大致相同的速度到可能慢 2-4 倍,具体取决于实现和硬件,因此您可以期望在具有许多内核的机器上加速,但只有 2 或 4 个可能不会更好。
如分布M不平滑,因此在第一步中创建的分区具有非常不同的大小,它会工作得很糟糕,因为某些线程会得到更多的工作。一个简单的策略是创建 say 10 * T分区,而不仅仅是 T并让第二遍中的线程都从相同的分区队列中消耗,直到完成。通过这种方式,您可以更均匀地分配工作,除非数组 M非常拥挤。在这种情况下,您可能会考虑对第一步进行细化,它首先基本上创建元素的分桶直方图,然后是一个减少阶段,该阶段查看组合直方图以创建良好的分区。
本质上,我们只是逐步将第一阶段细化为一种并行排序/分区算法,对此已有大量文献。您甚至可能会发现完整(并行)排序是最快的,因为它将在位设置阶段非常有帮助,因为访问将按顺序进行并且具有最佳的空间局部性(分别有助于预取和缓存)。
0 ... 也来自“分配长度为 N 的私有(private)数组”步骤,尽管这可能非常快。
1 概念上最简单的合并形式是简单地复制每个线程的 M 分区,这样您就拥有了所有 M 的连续分区。 ,但在实践中,如果分区很大,您可以将分区留在原处并将它们链接在一起,这会给使用代码增加一些复杂性,但避免了压缩步骤。
2 从线程的角度来看,要使其真正不相交,您需要确保N 的分区。落在“字节边界”上,甚至缓存行边界上以避免错误共享(尽管后者可能不是大问题,因为它只发生在每个分区的边缘,并且处理顺序意味着您不太可能发生争用)。
4 在实践中,使用共享的基线并发解决方案的确切“顺序”N很难定义,因为会有争用,所以 O(M/T)如果足够大,缩放会分解 T .如果我们假设 N相当大和T仅限于最多十几个核心的典型硬件并发,因此它可能是一个不错的近似值。
关于c++ - 如何有效地并行设置位 vector 的位?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/45556086/
我正在学习如何使用Nokogiri,根据这段代码我遇到了一些问题:require'rubygems'require'mechanize'post_agent=WWW::Mechanize.newpost_page=post_agent.get('http://www.vbulletin.org/forum/showthread.php?t=230708')puts"\nabsolutepathwithtbodygivesnil"putspost_page.parser.xpath('/html/body/div/div/div/div/div/table/tbody/tr/td/div
总的来说,我对ruby还比较陌生,我正在为我正在创建的对象编写一些rspec测试用例。许多测试用例都非常基础,我只是想确保正确填充和返回值。我想知道是否有办法使用循环结构来执行此操作。不必为我要测试的每个方法都设置一个assertEquals。例如:describeitem,"TestingtheItem"doit"willhaveanullvaluetostart"doitem=Item.new#HereIcoulddotheitem.name.shouldbe_nil#thenIcoulddoitem.category.shouldbe_nilendend但我想要一些方法来使用
关闭。这个问题是opinion-based.它目前不接受答案。想要改进这个问题?更新问题,以便editingthispost可以用事实和引用来回答它.关闭4年前。Improvethisquestion我想在固定时间创建一系列低音和高音调的哔哔声。例如:在150毫秒时发出高音调的蜂鸣声在151毫秒时发出低音调的蜂鸣声200毫秒时发出低音调的蜂鸣声250毫秒的高音调蜂鸣声有没有办法在Ruby或Python中做到这一点?我真的不在乎输出编码是什么(.wav、.mp3、.ogg等等),但我确实想创建一个输出文件。
给定这段代码defcreate@upgrades=User.update_all(["role=?","upgraded"],:id=>params[:upgrade])redirect_toadmin_upgrades_path,:notice=>"Successfullyupgradeduser."end我如何在该操作中实际验证它们是否已保存或未重定向到适当的页面和消息? 最佳答案 在Rails3中,update_all不返回任何有意义的信息,除了已更新的记录数(这可能取决于您的DBMS是否返回该信息)。http://ar.ru
我在我的项目目录中完成了compasscreate.和compassinitrails。几个问题:我已将我的.sass文件放在public/stylesheets中。这是放置它们的正确位置吗?当我运行compasswatch时,它不会自动编译这些.sass文件。我必须手动指定文件:compasswatchpublic/stylesheets/myfile.sass等。如何让它自动运行?文件ie.css、print.css和screen.css已放在stylesheets/compiled。如何在编译后不让它们重新出现的情况下删除它们?我自己编译的.sass文件编译成compiled/t
我正在寻找执行以下操作的正确语法(在Perl、Shell或Ruby中):#variabletoaccessthedatalinesappendedasafileEND_OF_SCRIPT_MARKERrawdatastartshereanditcontinues. 最佳答案 Perl用__DATA__做这个:#!/usr/bin/perlusestrict;usewarnings;while(){print;}__DATA__Texttoprintgoeshere 关于ruby-如何将脚
Rackup通过Rack的默认处理程序成功运行任何Rack应用程序。例如:classRackAppdefcall(environment)['200',{'Content-Type'=>'text/html'},["Helloworld"]]endendrunRackApp.new但是当最后一行更改为使用Rack的内置CGI处理程序时,rackup给出“NoMethodErrorat/undefinedmethod`call'fornil:NilClass”:Rack::Handler::CGI.runRackApp.newRack的其他内置处理程序也提出了同样的反对意见。例如Rack
在选择我想要运行操作的频率时,唯一的选项是“每天”、“每小时”和“每10分钟”。谢谢!我想为我的Rails3.1应用程序运行调度程序。 最佳答案 这不是一个优雅的解决方案,但您可以安排它每天运行,并在实际开始工作之前检查日期是否为当月的第一天。 关于ruby-如何每月在Heroku运行一次Scheduler插件?,我们在StackOverflow上找到一个类似的问题: https://stackoverflow.com/questions/8692687/
我有一个对象has_many应呈现为xml的子对象。这不是问题。我的问题是我创建了一个Hash包含此数据,就像解析器需要它一样。但是rails自动将整个文件包含在.........我需要摆脱type="array"和我该如何处理?我没有在文档中找到任何内容。 最佳答案 我遇到了同样的问题;这是我的XML:我在用这个:entries.to_xml将散列数据转换为XML,但这会将条目的数据包装到中所以我修改了:entries.to_xml(root:"Contacts")但这仍然将转换后的XML包装在“联系人”中,将我的XML代码修改为
我有一大串格式化数据(例如JSON),我想使用Psychinruby同时保留格式转储到YAML。基本上,我希望JSON使用literalstyle出现在YAML中:---json:|{"page":1,"results":["item","another"],"total_pages":0}但是,当我使用YAML.dump时,它不使用文字样式。我得到这样的东西:---json:!"{\n\"page\":1,\n\"results\":[\n\"item\",\"another\"\n],\n\"total_pages\":0\n}\n"我如何告诉Psych以想要的样式转储标量?解