在RPC系统中寻找延迟的来源

时间:2018-06-19 14:28:18

标签: rpc latency

顾名思义,我正在PC(windows x64)和运行ubuntu的嵌入式linux PC之间使用一个简单的RPC系统。嵌入式linux pc是RPC服务器,而PC是RPC客户端。 RPC框架为:erpc

我注意到我获得的交易速率特别低-大约20笔交易/秒。

问题绝对不是与硬件相关的,因为我有一个备用RPC系统(我正尝试用有争议的RPC系统替换),使用完全相同的硬件配置,它可以轻松地以每秒1000个以上的速度进行交易。

为了进一步证明这一点,我还编写了一个简单的python脚本,该脚本可以根据开关作为简单的套接字客户端或服务器。我将其作为服务器和PC上的客户端在嵌入式计算机上运行。该脚本只是让客户端向服务器发送一些随机数据,服务器又将数据发送回服务器。客户执行此操作几百次,并据此确定交易速率。传输的数据量与erpc使用的顺序相同。使用此设置,我可以获得每秒3000笔以上的交易。

所讨论的RPC系统是半双工的。仅使用一个线程。服务器recv,循环处理请求和send响应。 在测试期间仅使用一个插座。即在循环过程中,没有closeaccept出现。没有其他IO发生。至少,出于这些测试的目的,我已经将其重构为不执行任何其他IO。

在Windows客户端上,我有一个使用概要分析运行的python单元测试。结果似乎并不表明问题出在客户端上。

ncalls  tottime  percall  cumtime  percall filename:lineno(function)
     1    0.000    0.000   23.998   23.998 runner.py:105(pytest_runtest_call)
     1    0.000    0.000   23.998   23.998 python.py:1313(runtest)
     1    0.000    0.000   23.998   23.998 __init__.py:603(__call__)
     1    0.000    0.000   23.998   23.998 __init__.py:219(_hookexec)
     1    0.000    0.000   23.998   23.998 __init__.py:213(<lambda>)
     1    0.000    0.000   23.998   23.998 callers.py:151(_multicall)
     1    0.000    0.000   23.998   23.998 python.py:183(pytest_pyfunc_call)
     1    0.003    0.003   23.998   23.998 test_static_if.py:4(test_read_version)
   400    0.014    0.000   23.993    0.060 client.py:16(get_version)
   400    0.017    0.000   23.942    0.060 client.py:79(perform_request)
   400    0.006    0.000   23.828    0.060 transport.py:75(receive)
   800    0.016    0.000   23.820    0.030 transport.py:139(_base_receive)
   800   23.803    0.030   23.803    0.030 {method 'recv' of '_socket.socket' objects}
   400    0.007    0.000    0.061    0.000 transport.py:65(send)
   400    0.002    0.000    0.053    0.000 transport.py:135(_base_send)
   400    0.050    0.000    0.050    0.000 {method 'sendall' of '_socket.socket' objects}
   400    0.012    0.000    0.032    0.000 basic_codec.py:113(start_read_message)
   400    0.006    0.000    0.015    0.000 basic_codec.py:39(start_write_message)
  1600    0.007    0.000    0.015    0.000 basic_codec.py:130(_read)
   800    0.002    0.000    0.012    0.000 basic_codec.py:156(read_uint32)

该服务器是C ++应用程序。我尝试使用gprof对其进行性能分析,但是结果实际上表明该应用程序根本没有消耗时间。在详细了解了gprof的工作方式以及gprof不会累积在系统调用上花费的时间之后,这表明该程序(显然)受IO约束,并且绝大部分时间都花费在阻止系统调用上。

为简洁起见,我不会在此处添加整个输出,但以下是摘录:

Flat profile:

Each sample counts as 0.01 seconds.
 no time accumulated

  %   cumulative   self              self     total           
 time   seconds   seconds    calls  Ts/call  Ts/call  name    
  0.00      0.00     0.00     2407     0.00     0.00  erpc::MessageBuffer::get()
  0.00      0.00     0.00     2400     0.00     0.00  erpc::MessageBuffer::setUsed(unsigned short)
  0.00      0.00     0.00     2000     0.00     0.00  erpc::MessageBuffer::getUsed() const
  0.00      0.00     0.00     1600     0.00     0.00  erpc::MessageBuffer::Cursor::write(void const*, unsigned int)
  0.00      0.00     0.00     1201     0.00     0.00  erpc::Codec::getBuffer()
  0.00      0.00     0.00      803     0.00     0.00  erpc::MessageBuffer::Cursor::set(erpc::MessageBuffer*)
  0.00      0.00     0.00      803     0.00     0.00  erpc::MessageBuffer::getLength() const
  0.00      0.00     0.00      802     0.00     0.00  erpc::Codec::reset()
  0.00      0.00     0.00      801     0.00     0.00  erpc::TCPTransport::underlyingReceive(unsigned char*, unsigned int)
  0.00      0.00     0.00      800     0.00     0.00  erpc::TCPTransport::underlyingSend(unsigned char const*, unsigned int)
  0.00      0.00     0.00      800     0.00     0.00  erpc::BasicCodec::read(unsigned int*)
  0.00      0.00     0.00      800     0.00     0.00  erpc::BasicCodec::write(int)
  0.00      0.00     0.00      800     0.00     0.00  erpc::BasicCodec::write(unsigned int)
  0.00      0.00     0.00      800     0.00     0.00  erpc::MessageBuffer::Cursor::read(void*, unsigned int)
  0.00      0.00     0.00      800     0.00     0.00  erpc::Service::getServiceId() const
  0.00      0.00     0.00      403     0.00     0.00  erpc::Service::getNext()
  0.00      0.00     0.00      401     0.00     0.00  erpc::SimpleServer::runInternal(erpc::Codec*)
  0.00      0.00     0.00      401     0.00     0.00  erpc::TCPTransport::accept()
  0.00      0.00     0.00      401     0.00     0.00  erpc::TCPTransport::receive(erpc::MessageBuffer*)
  0.00      0.00     0.00      401     0.00     0.00  erpc::FramedTransport::receive(erpc::MessageBuffer*)
  0.00      0.00     0.00      400     0.00     0.00  write_p_version_t_struct(erpc::Codec*, p_version_t const*)
  0.00      0.00     0.00      400     0.00     0.00  StaticIF_service::handleInvocation(unsigned int, unsigned int, erpc::Codec*, erpc::MessageBufferFactory*)
  0.00      0.00     0.00      400     0.00     0.00  StaticIF_service::get_version_shim(erpc::Codec*, erpc::MessageBufferFactory*, unsigned int)
  0.00      0.00     0.00      400     0.00     0.00  erpc::BasicCodec::endReadMessage()
  0.00      0.00     0.00      400     0.00     0.00  erpc::BasicCodec::endWriteStruct()
  0.00      0.00     0.00      400     0.00     0.00  erpc::BasicCodec::endWriteMessage()
  0.00      0.00     0.00      400     0.00     0.00  erpc::BasicCodec::startReadMessage(erpc::_message_type*, unsigned int*, unsigned int*, unsigned int*)
  0.00      0.00     0.00      400     0.00     0.00  erpc::BasicCodec::startWriteStruct()
  0.00      0.00     0.00      400     0.00     0.00  erpc::BasicCodec::startWriteMessage(erpc::_message_type, unsigned int, unsigned int, unsigned int)
  0.00      0.00     0.00      400     0.00     0.00  erpc::FramedTransport::send(erpc::MessageBuffer*)
  0.00      0.00     0.00      400     0.00     0.00  erpc::MessageBufferFactory::prepareServerBufferForSend(erpc::MessageBuffer*)
  0.00      0.00     0.00      400     0.00     0.00  erpc::Server::processMessage(erpc::Codec*, erpc::_message_type&)
  0.00      0.00     0.00      400     0.00     0.00  erpc::Server::findServiceWithId(unsigned int)
  0.00      0.00     0.00      400     0.00     0.00  get_version
  0.00      0.00     0.00        5     0.00     0.00  erpc::ManuallyConstructed<erpc::SimpleServer>::get()
  0.00      0.00     0.00        4     0.00     0.00  operator new(unsigned int, void*)
  0.00      0.00     0.00        3     0.00     0.00  erpc::ManuallyConstructed<erpc::SimpleServer>::operator->()
  0.00      0.00     0.00        2     0.00     0.00  erpc::ManuallyConstructed<erpc::TCPTransport>::get()
  0.00      0.00     0.00        2     0.00     0.00  erpc::ManuallyConstructed<erpc::BasicCodecFactory>::get()
  0.00      0.00     0.00        2     0.00     0.00  erpc::Server::addService(erpc::Service*)
  0.00      0.00     0.00        2     0.00     0.00  erpc::Service::Service(unsigned int)
  0.00      0.00     0.00        2     0.00     0.00  erpc::Service::~Service()
  0.00      0.00     0.00        2     0.00     0.00  erpc_add_service_to_server
  0.00      0.00     0.00        1     0.00     0.00  _GLOBAL__sub_I__Z5usagev

使用strace,此问题在每个请求的第一个recv中变得明显。对于上下文,首先发送初始报头,该报头指示请求正确包含的数据量。

以下是输出的摘录(完整输出为2000行)。 我使用了-r-T-C开关,它们显示每个呼叫的相对时间戳,打印每个呼叫所花费的时间并分别显示摘要。

在事务循环中:

 0.000161 recv(4, "\10\0", 2, 0)    = 2 <0.059478>
 0.059589 recv(4, "q\1\1\2\0\1\0\0", 8, 0) = 8 <0.000047>
 0.000167 send(4, "\20\0", 2, 0)    = 2 <0.000073>
 0.000183 send(4, "q\1\1\2\2\1\0\0\235\256\322\2664\22\0\0", 16, 0) = 16 <0.000050>
 0.000160 recv(4, "\10\0", 2, 0)    = 2 <0.059513>
 0.059625 recv(4, "r\1\1\2\0\1\0\0", 8, 0) = 8 <0.000046>
 0.000167 send(4, "\20\0", 2, 0)    = 2 <0.000071>
 0.000182 send(4, "r\1\1\2\2\1\0\0\235\256\322\2664\22\0\0", 16, 0) = 16 <0.000049>
 0.000161 recv(4, "\10\0", 2, 0)    = 2 <0.059059>
 0.059172 recv(4, "s\1\1\2\0\1\0\0", 8, 0) = 8 <0.000047>
 0.000183 send(4, "\20\0", 2, 0)    = 2 <0.000073>
 0.000183 send(4, "s\1\1\2\2\1\0\0\235\256\322\2664\22\0\0", 16, 0) = 16 <0.000049>
 0.000161 recv(4, "\10\0", 2, 0)    = 2 <0.059330>
 0.059441 recv(4, "t\1\1\2\0\1\0\0", 8, 0) = 8 <0.000046>
 0.000166 send(4, "\20\0", 2, 0)    = 2 <0.000072>
 0.000182 send(4, "t\1\1\2\2\1\0\0\235\256\322\2664\22\0\0", 16, 0) = 16 <0.000050>
 0.000163 recv(4, "\10\0", 2, 0)    = 2 <0.059506>
 0.059618 recv(4, "u\1\1\2\0\1\0\0", 8, 0) = 8 <0.000046>
 0.000166 send(4, "\20\0", 2, 0)    = 2 <0.000070>
 0.000181 send(4, "u\1\1\2\2\1\0\0\235\256\322\2664\22\0\0", 16, 0) = 16 <0.000049>
 0.000160 recv(4, "\10\0", 2, 0)    = 2 <0.059359>
 0.059488 recv(4, "v\1\1\2\0\1\0\0", 8, 0) = 8 <0.000048>
 0.000175 send(4, "\20\0", 2, 0)    = 2 <0.000077>
 0.000189 send(4, "v\1\1\2\2\1\0\0\235\256\322\2664\22\0\0", 16, 0) = 16 <0.000051>
 0.000165 recv(4, "\10\0", 2, 0)    = 2 <0.059496>
 0.059612 recv(4, "w\1\1\2\0\1\0\0", 8, 0) = 8 <0.000046>
 0.000170 send(4, "\20\0", 2, 0)    = 2 <0.000074>
 0.000182 send(4, "w\1\1\2\2\1\0\0\235\256\322\2664\22\0\0", 16, 0) = 16 <0.000050>

摘要:

% time     seconds  usecs/call     calls    errors syscall
------ ----------- ----------- --------- --------- ----------------
 98.59    0.010000          12       801           recv
  1.41    0.000143           0       800           send
  0.00    0.000000           0        12           read
  0.00    0.000000           0         3           write
  0.00    0.000000           0        25        19 open
  0.00    0.000000           0         7           close
  0.00    0.000000           0         1           execve
  0.00    0.000000           0         8           lseek
  0.00    0.000000           0         6         6 access
  0.00    0.000000           0         3           brk
  0.00    0.000000           0         1           readlink
  0.00    0.000000           0         1           munmap
  0.00    0.000000           0         2           setitimer
  0.00    0.000000           0         1           uname
  0.00    0.000000           0         9           mprotect
  0.00    0.000000           0         5           writev
  0.00    0.000000           0         2           rt_sigaction
  0.00    0.000000           0        16           mmap2
  0.00    0.000000           0        16        15 stat64
  0.00    0.000000           0         6           fstat64
  0.00    0.000000           0         1           socket
  0.00    0.000000           0         1           bind
  0.00    0.000000           0         1           listen
  0.00    0.000000           0         1           accept
  0.00    0.000000           0         1           setsockopt
  0.00    0.000000           0         1           set_tls
------ ----------- ----------- --------- --------- ----------------
100.00    0.010143                  1731        40 total

顺便说一句,我不确定我是否完全理解摘要。摘要表明,与每次调用recv所指示的时间相比,recv的发生速度非常快。

在第一个recv中所花费的时间似乎导致了每次调用将近60毫秒使RPC系统瘫痪。我读错了吗?我不确定单位,所以我猜是几秒钟。

因此,在对客户端和服务器进行性能分析之后,似乎在recv中花费了大量时间。 如果我们假设在服务器端最初的recv上花费了额外的时间是因为客户端仍在处理某些东西,而尚未send,那在对客户端进行概要分析时应该已经显示出来。

对于如何进一步调试它的任何建议将不胜感激。

谢谢!

0 个答案:

没有答案