在负载下减少 NetNamedPipe 的 CPU 使用
Posted
技术标签:
【中文标题】在负载下减少 NetNamedPipe 的 CPU 使用【英文标题】:reducing the CPU use of NetNamedPipe when under load 【发布时间】:2019-09-09 03:00:04 【问题描述】:我有一个 Windows 服务,它使用 NetNamedPipe 与同一台机器上的其他进程进行通信。它工作正常,除了一个问题:高 CPU 使用率。我能做些什么来减少这种使用吗?
为了更好地理解这个问题,我制作了一个简单的测试程序,它通过命名管道与自己对话并跟踪自己的 CPU 使用情况。当不经常使用命名管道(每秒 1 次操作)时,CPU 使用率非常低。当频繁使用命名管道(每秒数千次操作)时,CPU 使用率会增加。
这里是一些演示该行为的示例输出。 (注意,CPU 使用的是Process
> % Processor Time
计数器,这并不像您在任务管理器中看到的 CPU 使用那么简单。)
NetNamedPipe Passed: 31309 Failed: 0 Elapsed: 10.4 s Rate: 3000 Hz Process CPU: 30.0 %
NetNamedPipe Passed: 13 Failed: 0 Elapsed: 11.0 s Rate: 1 Hz Process CPU: 0.9 %
理想情况下,我想继续使用 NetNamedPipe,但要减少 CPU 使用率。我已经尝试使用 Stack Overflow 和其他地方的想法来调整 the optional settings of NetNamedPipeBinding,但无法减少 CPU 的使用。也许我错过了什么?
我意识到,很可能,我可能不得不做一些更激烈的事情。我可能需要以“捆绑”的形式发送更少、更大的消息。或者我可能需要使用不同的进程间通信方式。任何有关调查内容的建议将不胜感激。
我的测试程序代码如下。它针对 .NET Framework 4.7.2。我一直在 Windows 10 上运行。
程序.cs
using System;
using System.Diagnostics;
using System.Threading;
namespace IpcExperiments
class Program
private static readonly string MyName = "Alice";
private static readonly string ProcessName = "IpcExperiments";
private static readonly double DesiredRate = 3000; // In Hz
// private static readonly double DesiredRate = Double.MaxValue; // Go as fast as possible!
private static PerformanceCounter ProcessCpu = null;
static void Main(string[] args)
ProcessCpu = new PerformanceCounter("Process", "% Processor Time", ProcessName);
Test(new Experiments.NetNamedPipe(), MyName, DesiredRate);
// Optionally, add other tests here.
Console.Write("\r ");
Console.WriteLine();
Console.WriteLine("All tests complete! Press Enter to finish.");
Console.ReadLine();
private static void Test(Experiments.IIpcExperiment experiment, string myName, double desiredRate = Double.MaxValue)
int i = 0;
int successes = 0;
int fails = 0;
double elapsed = 0;
double rate = 0;
double thisCpu = 0;
double avgCpu = 0;
double cpuCount = 0;
string matchingName = String.Format("Hello 0!", myName);
string experimentName = experiment.GetExperimentName();
Console.Write("\rCreating 0...", experimentName);
experiment.Setup();
DateTime startTime = DateTime.Now;
DateTime nextCpuRead = DateTime.MinValue;
while (!Console.KeyAvailable)
if (experiment.SayHello(myName).Equals(matchingName))
successes++;
else
fails++;
if (nextCpuRead < DateTime.Now)
thisCpu = ProcessCpu.NextValue();
if (cpuCount == 0)
avgCpu = thisCpu;
else
avgCpu = ((avgCpu * cpuCount) + thisCpu) / (cpuCount + 1);
cpuCount++;
nextCpuRead = DateTime.Now.AddSeconds(1);
elapsed = (DateTime.Now - startTime).TotalSeconds;
rate = ((double)i) / elapsed;
Console.Write("\r0\tPassed: 1\tFailed: 2\tElapsed: 3:0.0 s\tRate: 4:0 Hz\t Process CPU: 5:0.0 %"
, experimentName
, successes
, fails
, elapsed
, rate
, avgCpu);
while (rate > desiredRate && !Console.KeyAvailable)
Thread.Sleep(1);
elapsed = (DateTime.Now - startTime).TotalSeconds;
rate = ((double)i) / elapsed;
i++;
Console.ReadKey(true);
Console.WriteLine();
Console.Write("\rDisposing 0...", experimentName);
experiment.Shutdown();
IIpcExperiment.cs
namespace IpcExperiments.Experiments
interface IIpcExperiment
string GetExperimentName();
void Setup();
void Shutdown();
string SayHello(string myName);
NetNamedPipe.cs
using System;
using System.ServiceModel;
namespace IpcExperiments.Experiments
[ServiceContract]
public interface INetNamedPipe
[OperationContract]
string SayHello(string myName);
public class IpcInterface : INetNamedPipe
public string SayHello(string myName)
return String.Format("Hello 0!", myName);
public class NetNamedPipe : IIpcExperiment
private ServiceHost Host;
private INetNamedPipe Client;
public void Setup()
SetupHost();
SetupClient();
public void Shutdown()
Host.Close();
public string GetExperimentName()
return "NetNamedPipe";
public string SayHello(string myName)
return Client.SayHello(myName);
private void SetupHost()
Host = new ServiceHost(typeof(IpcInterface),
new Uri[]
new Uri(@"net.pipe://localhost")
);
NetNamedPipeBinding nnpb = new NetNamedPipeBinding();
Host.AddServiceEndpoint(typeof(INetNamedPipe)
, nnpb
, "NetNamedPipeExample");
Host.Open();
private void SetupClient()
NetNamedPipeBinding nnpb = new NetNamedPipeBinding();
ChannelFactory<INetNamedPipe> pipeFactory =
new ChannelFactory<INetNamedPipe>(
nnpb,
new EndpointAddress(@"net.pipe://localhost/NetNamedPipeExample"));
Client = pipeFactory.CreateChannel();
【问题讨论】:
【参考方案1】:这就是我最终解决这个问题的方法。
在修复之前,在上述问题的示例代码中,我反复调用SayHello
,这样做的开销消耗了大量的 CPU。
修复后,我通过单个Stream
获得相同的数据。我怀疑设置流的 CPU 开销大致相同,但流只需要设置一次。总体 CPU 使用率要低得多。
WCF 命名管道支持流,因此我不必放弃使用命名管道。
您可以阅读有关流式传输 here 的信息,或者如果该链接失效,请将 TransferMode.Streaming
放入您最喜欢的搜索引擎。
我的流需要是“无限的”,这样它才能永远推送数据,所以我需要创建一个自定义 Stream
。 This answer on Stack Overflow 帮助指导我。
我还有一些粗糙的地方需要解决,但是 CPU 使用问题(即这个问题的症结所在)似乎已经通过这种方法解决了。
【讨论】:
以上是关于在负载下减少 NetNamedPipe 的 CPU 使用的主要内容,如果未能解决你的问题,请参考以下文章
django web应用runserver模式下cpu占用高解决办法