命名管道性能问题 [英] Named pipes performance issues

查看:406
本文介绍了命名管道性能问题的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我使用命名管道来进行C#和Delphi之间的程序间通信。 C#使用 System.IO.Pipes 包,而Delphi则使用 Libby的pipes.pas 。不幸的是,通信是高性能的:Profiling告诉我,通信占整个运行时的72%,其余的通过计算使用。

我能够找到一个可能占用的问题资源:如果我没有在Delphi中明确断开发送客户端的连接,那么 C#根本不会收到任何数据。



Delphi发送)



  FClient1.Write(msg [1],Length(msg)); 
FClient1.FlushPipeBuffers;
FClient1.WaitForReply(20);
FClient1.Disconnect; //断开信号通知C#写入完成
FClient1.Connect; //再次连接以防止同步问题



C#(receive)



  //等待客户端连接
stc.pipeServer.WaitForConnection();
while(reconnect_attempts< MAX_RECONNECT_ATTEMPTS)//
{
string tmp = sr.ReadLine();

//如果结果为空,请再次尝试< MAX_RECONNECT_ATTEMPTS>次
//,所以你可以消除只有一个空请求的机会
while(tmp!= null)//&& result!= tmp)
{
tmp = sr.ReadLine();
result + = tmp;
}
// sleep,increment reconnect,write debugging ...
}
stc.pipeServer.Close();

尽管我猜到重新连接是昂贵的,但我并不完全相信。一个数据流(大约1 / 11kb)需要130个(分别为270kb,11kb)的总数(发送和接收)。



我的问题是

是否有必要强制断开管道以指示客户端完成写入?就我的观察而言,只有在使用libby的发送时才需要这样做。还有其他可能的原因导致表现不佳吗?感谢提前。



作为补充,这里发送和接收完成反之亦然:



C# )



  stc.pipeClient.Connect(); 
StreamWriter sw = new StreamWriter(stc.pipeClient);
//sw.AutoFlush = true;
sw.WriteLine(msg);
sw.Flush();
stc.pipeClient.WaitForPipeDrain(); //等待另一端读取所有字节
//既不断开也不处理



Delphi(收到)



  SetLength(S,Stream.Size); Stream.Read(S [1],Length(S)); 
FPipeBuffer:= FPipeBuffer + S; {TODO 2:switch case ID}
//如果XML完成,即以结束校验和结束
if(IsFullMessage())then
begin
// end reading ,设置标志
FIsPipeReady:= true;
end


解决方案

经过很多(手动)剖析,我想出了两个关于这个问题的见解:


  1. Libby的管道是一个复杂的野兽。由于它似乎使用多个线程并且显示了一个关于它的使用的奇怪的行为,手动使用WinApi毕竟更加方便。此外,实际交流所采取的表现也增加了。换句话说:在这样一个比较简单的IPC的情况下,libby的管道似乎比WinApi慢。

  2. 使用stdout& stdin似乎比命名管道更快。

但是,我必须补充说,我还是有点困惑,不能告诉这是否是真的,或者我一直在纠正错误的数字。



以下是一个简单的例子,说明如何在Delphi中实现WinApi可能如下所示:

  //设置管道,您将需要为每个方向一个
// init handle with 0
CreatePipe(ReadPipe1, // hReadPipe
WritePipe1,// hWritePIpe
@SecurityAttributes,// Security
PIPE_SIZE)// Size

// setup Startupinfo
FillChar(StartupInfo ,Sizeof(StartupInfo),0);
StartupInfo.cb:= Sizeof(StartupInfo);
StartupInfo.dwFlags:= STARTF_USESHOWWINDOW或STARTF_USESTDHANDLES;
StartupInfo.hStdInput:= ReadPipe1;
StartupInfo.hStdOutput:= WritePipe2;
StartupInfo.wShowWindow:= SW_HIDE;

// CreateProcess [...]

//读
Win32Check(
ReadFile(
ReadPipe1,// source
(@outputBuffer [1])^,//缓冲区指针
PIPE_BUFFER_SIZE,// size
bytesRead,//返回实际读取的字节
nil //默认重叠
));
//发送
Win32Check(
WriteFile(
WritePipe2,
(@msg [1])^,// lpBuffer - workarround以避免类型转换
NumberOfBytesToWrite,
bytesWritten,// lpNumberOfBytesWritten
nil //重叠的
));


I'm using named pipes for inter-procedural communication between C# and Delphi. C# uses the System.IO.Pipes package, whereas Delphi makes use of Libby's pipes.pas. Unfortunately, the communication is all but high-performance: Profiling showed me that the communication takes 72% of the whole runtime, the rest is used by calculations.
I was able to locate one problem that could take up resources: If I don't explicitly disconnect the sending client's connection in Delphi, C# doesn't receive any data at all.

Delphi (sending)

FClient1.Write(msg[1], Length(msg));
FClient1.FlushPipeBuffers;
FClient1.WaitForReply(20);
FClient1.Disconnect;   // disconnect to signalize C# that the writing is finished
FClient1.Connect;      // connect again to prevent synchronization problems

C# (receiving)

// Wait for a client to connect
stc.pipeServer.WaitForConnection();
while (reconnect_attempts < MAX_RECONNECT_ATTEMPTS) // 
{
   string tmp = sr.ReadLine();

   // if result is empty, try again for <MAX_RECONNECT_ATTEMPTS> times
   // so you can eliminate the chance that there's just a single empty request
   while (tmp != null)// && result != tmp)
   {
      tmp = sr.ReadLine();
      result += tmp;
   }
   // sleep, increment reconnect, write debugging...
}
stc.pipeServer.Close();

Even though I guess that the reconnecting is expensive, I'm not entirely sure about it. One flow of data (roughly 1 / 11 kb) takes 130 (respectively 270ms for the 11kb) total (sending & receiving).

My question would be:
Is it necessary to force-disconnect the pipes to signalize that the client is done writing? As far as my observations go, this is only necessary when sending with libby's. Are there any other possible causes for the poor performance? Thanks in advance.

As an addition, here's the sending and receiving done vice versa:

C# (sending)

 stc.pipeClient.Connect();
 StreamWriter sw = new StreamWriter(stc.pipeClient);
 //sw.AutoFlush = true;
 sw.WriteLine(msg);
 sw.Flush();
 stc.pipeClient.WaitForPipeDrain();  // waits for the other end to read all bytes 
 // neither disconnect nor dispose

Delphi (receiving)

 SetLength(S, Stream.Size);   Stream.Read(S[1], Length(S));  
 FPipeBuffer := FPipeBuffer + S;   { TODO 2 : switch case ID }   
// if the XML is complete, i.e. ends with the closing checksum   
if (IsFullMessage()) then
begin
   // end reading, set flag
   FIsPipeReady := true;
end

解决方案

After a lot of (manual) profiling, I came up with two insights about the problem:

  1. Libby's pipes is a complex beast. Since it seems to use multiple threads and shows a weird behavior concerning its usage, the manual use of the WinApi was more convienient after all. Furthermore, the performance taken by the actual communication increased. In other words: In a relatively simple IPC-scenario like this, libby's pipes seem to be slower than the WinApi.
  2. Annonymous pipes / using the stdout & stdin seem to be even faster than named pipes.

However, I must add that I still am sort of confused and can't tell whether this is true or I've been crunching the wrong numbers here.

Here's an easy example of how the WinApi implementation in Delphi could look like:

// setup pipes, you'll need one for each direction
// init handles with 0
    CreatePipe(ReadPipe1,       // hReadpipe
               WritePipe1,      // hWritePIpe
               @SecurityAttributes,        // Security
               PIPE_SIZE)                  // Size

    // setup Startupinfo
    FillChar(StartupInfo, Sizeof(StartupInfo), 0);
    StartupInfo.cb := Sizeof(StartupInfo);
    StartupInfo.dwFlags := STARTF_USESHOWWINDOW or STARTF_USESTDHANDLES;
    StartupInfo.hStdInput := ReadPipe1;
    StartupInfo.hStdOutput := WritePipe2;
    StartupInfo.wShowWindow :=  SW_HIDE; 

    // CreateProcess [...]

    // read
    Win32Check(
            ReadFile(
                  ReadPipe1,  // source
                  (@outputBuffer[1])^,               // buffer-pointer
                  PIPE_BUFFER_SIZE,                 // size
                  bytesRead,                       // returns bytes actually read
                  nil                             // overlapped on default
                  ));
    // send           
    Win32Check(
            WriteFile(
                WritePipe2,
                (@msg[1])^,         // lpBuffer - workarround to avoid type cast
                NumberOfBytesToWrite,
                bytesWritten,       // lpNumberOfBytesWritten
                nil                 // Overlapped   
                ));                          

这篇关于命名管道性能问题的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆