我编写了一个非常简单的“字数统计”程序,它读取文件并计算文件中每个字的出现次数。这是代码的一部分:
class Alaki
{
private static List<string> input = new List<string>();
private static void exec(int threadcount)
{
ParallelOptions options = new ParallelOptions();
options.MaxDegreeOfParallelism = threadcount;
Parallel.ForEach(Partitioner.Create(0, input.Count),options, (range) =>
{
var dic = new Dictionary<string, List<int>>();
for (int i = range.Item1; i < range.Item2; i++)
{
//make some delay!
//for (int x = 0; x < 400000; x++) ;
var tokens = input[i].Split();
foreach (var token in tokens)
{
if (!dic.ContainsKey(token))
dic[token] = new List<int>();
dic[token].Add(1);
}
}
});
}
public static void Main(String[] args)
{
StreamReader reader=new StreamReader((@"c:\txt-set\agg.txt"));
while(true)
{
var line=reader.ReadLine();
if(line==null)
break;
input.Add(line);
}
DateTime t0 = DateTime.Now;
exec(Environment.ProcessorCount);
Console.WriteLine("Parallel: " + (DateTime.Now - t0));
t0 = DateTime.Now;
exec(1);
Console.WriteLine("Serial: " + (DateTime.Now - t0));
}
}
它简单明了。我用字典来计算每个单词的出现次数。样式大致基于MapReduce编程模型。如您所见,每个任务都在使用自己的私有(private)字典。所以,没有共享变量;只是一堆自己计算单词的任务。以下是代码在四核 i7 CPU 上运行时的输出:
并行:00:00:01.6220927
序列号:00:00:02.0471171
加速比大约是1.25,悲剧了!但是当我在处理每一行时添加一些延迟时,我可以达到大约 4 的加速值。
在原来的无延迟并行执行中,CPU的利用率几乎没有达到30%,因此提速并不理想。但是,当我们添加一些延迟时,CPU 的利用率达到了 97%。
首先,我认为原因是程序的 IO 绑定(bind)性质(但我认为插入字典在某种程度上是 CPU 密集型的)并且这似乎合乎逻辑,因为所有线程都从共享内存总线读取数据.然而,令人惊讶的是,当我同时运行 4 个串行程序实例(没有延迟)时,CPU 的利用率达到了大约 raises,所有 4 个实例都在大约 2.3 秒内完成!
这意味着当代码在多处理配置中运行时,它达到了大约 3.5 的加速值,但是当它在多线程配置中运行时,加速约为 1.25。
你的想法是什么? 我的代码有什么问题吗?因为我认为根本没有共享数据,而且我认为代码不会遇到任何争用。 .NET 的运行时是否存在缺陷?
提前致谢。
最佳答案
Parallel.For 不会将输入分成 n block (其中 n 是 MaxDegreeOfParallelism) ;相反,它会创建许多小批处理并确保最多 n 被同时处理。 (这样一来,如果一个批处理需要很长时间才能处理,Parallel.For 仍可以在其他线程上运行。有关详细信息,请参阅 Parallelism in .NET - Part 5, Partioning of Work。)
由于这种设计,您的代码正在创建和丢弃数十个 Dictionary 对象、数百个 List 对象和数千个 String 对象。这给垃圾收集器带来了巨大的压力。
正在运行 PerfMonitor在我的电脑上报告说总运行时间的 43% 花在了 GC 上。如果您重写代码以使用更少的临时对象,您应该会看到所需的 4 倍加速。 PerfMonitor 报告的一些摘录如下:
Over 10% of the total CPU time was spent in the garbage collector. Most well tuned applications are in the 0-10% range. This is typically caused by an allocation pattern that allows objects to live just long enough to require an expensive Gen 2 collection.
This program had a peak GC heap allocation rate of over 10 MB/sec. This is quite high. It is not uncommon that this is simply a performance bug.
编辑:根据您的评论,我将尝试解释您报告的时间。在我的计算机上,使用 PerfMonitor,我测得 43% 到 52% 的时间花费在 GC 上。为简单起见,我们假设 50% 的 CPU 时间用于工作,50% 用于 GC。因此,如果我们使工作速度提高 4 倍(通过多线程)但保持 GC 的数量相同(这会发生,因为在并行和串行配置中处理的批处理数量恰好相同),最好的我们可以获得的改进是原始时间的 62.5%,即 1.6 倍。
但是,我们只看到了 1.25 倍的加速,因为默认情况下 GC 不是多线程的(在工作站 GC 中)。根据 Fundamentals of Garbage Collection ,所有托管线程在 Gen 0 或 Gen 1 收集期间暂停。 (并发和后台 GC,在 .NET 4 和 .NET 4.5 中,可以在后台线程上收集第 2 代。)你的程序只经历了 1.25× 加速(你看到 30% 的总体 CPU 使用率),因为线程花费了他们的大部分时间GC暂停时间(因为这个测试程序的内存分配模式很差)。
如果启用 server GC ,它将在多个线程上执行垃圾收集。如果我这样做,程序运行速度将提高 2 倍(CPU 使用率几乎为 100%)。
当您同时运行程序的四个实例时,每个实例都有自己的托管堆,并且四个进程的垃圾回收可以并行执行。这就是为什么您会看到 100% 的 CPU 使用率(每个进程使用一个 CPU 的 100%)。略长的总体时间(全部 2.3 秒 vs 一个 2.05 秒)可能是由于测量不准确、磁盘争用、加载文件花费的时间、必须初始化线程池、上下文切换的开销或其他一些原因环境因素。
关于c# - .NET 多线程与多处理 : Awful Parallel. ForEach 性能,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/13671053/
作为我的Rails应用程序的一部分,我编写了一个小导入程序,它从我们的LDAP系统中吸取数据并将其塞入一个用户表中。不幸的是,与LDAP相关的代码在遍历我们的32K用户时泄漏了大量内存,我一直无法弄清楚如何解决这个问题。这个问题似乎在某种程度上与LDAP库有关,因为当我删除对LDAP内容的调用时,内存使用情况会很好地稳定下来。此外,不断增加的对象是Net::BER::BerIdentifiedString和Net::BER::BerIdentifiedArray,它们都是LDAP库的一部分。当我运行导入时,内存使用量最终达到超过1GB的峰值。如果问题存在,我需要找到一些方法来更正我的代
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
是的,我知道最好使用webmock,但我想知道如何在RSpec中模拟此方法:defmethod_to_testurl=URI.parseurireq=Net::HTTP::Post.newurl.pathres=Net::HTTP.start(url.host,url.port)do|http|http.requestreq,foo:1endresend这是RSpec:let(:uri){'http://example.com'}specify'HTTPcall'dohttp=mock:httpNet::HTTP.stub!(:start).and_yieldhttphttp.shou
我收到这个错误:RuntimeError(自动加载常量Apps时检测到循环依赖当我使用多线程时。下面是我的代码。为什么会这样?我尝试多线程的原因是因为我正在编写一个HTML抓取应用程序。对Nokogiri::HTML(open())的调用是一个同步阻塞调用,需要1秒才能返回,我有100,000多个页面要访问,所以我试图运行多个线程来解决这个问题。有更好的方法吗?classToolsController0)app.website=array.join(',')putsapp.websiteelseapp.website="NONE"endapp.saveapps=Apps.order("
如何在ruby中调用C#dll? 最佳答案 我能想到几种可能性:为您的DLL编写(或找人编写)一个COM包装器,如果它还没有,则使用Ruby的WIN32OLE库来调用它;看看RubyCLR,其中一位作者是JohnLam,他继续在Microsoft从事IronRuby方面的工作。(估计不会再维护了,可能不支持.Net2.0以上的版本);正如其他地方已经提到的,看看使用IronRuby,如果这是您的技术选择。有一个主题是here.请注意,最后一篇文章实际上来自JohnLam(看起来像是2009年3月),他似乎很自在地断言RubyCL
我正在尝试在Ruby中复制Convert.ToBase64String()行为。这是我的C#代码:varsha1=newSHA1CryptoServiceProvider();varpasswordBytes=Encoding.UTF8.GetBytes("password");varpasswordHash=sha1.ComputeHash(passwordBytes);returnConvert.ToBase64String(passwordHash);//returns"W6ph5Mm5Pz8GgiULbPgzG37mj9g="当我在Ruby中尝试同样的事情时,我得到了相同sha
我目前正在使用以下方法获取页面的源代码:Net::HTTP.get(URI.parse(page.url))我还想获取HTTP状态,而无需发出第二个请求。有没有办法用另一种方法做到这一点?我一直在查看文档,但似乎找不到我要找的东西。 最佳答案 在我看来,除非您需要一些真正的低级访问或控制,否则最好使用Ruby的内置Open::URI模块:require'open-uri'io=open('http://www.example.org/')#=>#body=io.read[0,50]#=>"["200","OK"]io.base_ur
C#实现简易绘图工具一.引言实验目的:通过制作窗体应用程序(C#画图软件),熟悉基本的窗体设计过程以及控件设计,事件处理等,熟悉使用C#的winform窗体进行绘图的基本步骤,对于面向对象编程有更加深刻的体会.Tutorial任务设计一个具有基本功能的画图软件**·包括简单的新建文件,保存,重新绘图等功能**·实现一些基本图形的绘制,包括铅笔和基本形状等,学习橡皮工具的创建**·设计一个合理舒适的UI界面**注明:你可能需要先了解一些关于winform窗体应用程序绘图的基本知识,以及关于GDI+类和结构的知识二.实验环境Windows系统下的visualstudio2017C#窗体应用程序三.
1.错误信息:Errorresponsefromdaemon:Gethttps://registry-1.docker.io/v2/:net/http:requestcanceledwhilewaitingforconnection(Client.Timeoutexceededwhileawaitingheaders)或者:Errorresponsefromdaemon:Gethttps://registry-1.docker.io/v2/:net/http:TLShandshaketimeout2.报错原因:docker使用的镜像网址默认为国外,下载容易超时,需要修改成国内镜像地址(首先阿里
我正在尝试使用ruby编写一个双线程客户端,一个线程从套接字读取数据并将其打印出来,另一个线程读取本地数据并将其发送到远程服务器。我发现的问题是Ruby似乎无法捕获线程内的错误,这是一个示例:#!/usr/bin/rubyThread.new{loop{$stdout.puts"hi"abc.putsefsleep1}}loop{sleep1}显然,如果我在线程外键入abc.putsef,代码将永远不会运行,因为Ruby将报告“undefinedvariableabc”。但是,如果它在一个线程内,则没有错误报告。我的问题是,如何让Ruby捕获这样的错误?或者至少,报告线程中的错误?