4

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
4

3 回答 3

2

经过大量(手动)分析后,我对这个问题提出了两个见解:

  1. 利比的管道是一个复杂的野兽。由于它似乎使用多个线程并且在使用方面表现出奇怪的行为,因此手动使用 WinApi 毕竟更方便。此外,实际通信的性能有所提高。换句话说:在这样一个相对简单的 IPC 场景中,libby 的管道似乎比 WinApi 慢。
  2. 匿名管道/使用标准输出和标准输入似乎比命名管道更快。

但是,我必须补充一点,我仍然有点困惑,无法判断这是真的还是我在这里计算了错误的数字。

下面是一个简单的例子,说明 Delphi 中的 WinApi 实现如何:

// 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   
                ));                          
于 2012-10-22T13:00:31.770 回答
0

也许您可以将命名事件用于 IPC 信令。当这些是本地的 (TEvent.Create('local\myserver'); 当您需要在不同会话之间进行 IPC 时(例如客户端应用程序和后台 windows 服务),这些在 Win7 等中工作正常,您需要更多权限等(默认全局\由于UAC,无法在win7中使用?)。 http://docs.embarcadero.com/products/rad_studio/delphiAndcpp2009/HelpUpdate2/EN/html/devwin32/threadswaitingforatasktobecompleted_xml.html

例如:为每个连接创建一个事件(每个连接生成一个名称)。

或者看看不同的 IPC 命名管道 + 事件实现: https ://micksmix.wordpress.com/2011/06/27/named-pipes-unit-for-delphi/

顺便说一句:你提到你使用了分析,但你不能说什么最花时间?你使用了什么样的分析?不是像 AQtime (http://smartbear.com/products/free-tools/aqtime-standard) 或 AsmProfiler (http://code.google.com/p/asmprofiler/) 这样的“分析器”?

于 2012-10-16T12:45:11.457 回答
0

简单的改进可能是:首先发送要发送的字节数(以便接收者知道它可以期望多少数据)然后发送数据

于 2012-10-16T12:53:42.423 回答