ipc_cpu_perftest.cc 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432
  1. // Copyright 2017 The Chromium Authors. All rights reserved.
  2. // Use of this source code is governed by a BSD-style license that can be
  3. // found in the LICENSE file.
  4. #include <memory>
  5. #include <tuple>
  6. #include "base/bind.h"
  7. #include "base/check_op.h"
  8. #include "base/memory/raw_ptr.h"
  9. #include "base/process/process_metrics.h"
  10. #include "base/run_loop.h"
  11. #include "base/strings/stringprintf.h"
  12. #include "base/synchronization/waitable_event.h"
  13. #include "base/test/perf_log.h"
  14. #include "base/test/task_environment.h"
  15. #include "base/timer/timer.h"
  16. #include "ipc/ipc_channel_proxy.h"
  17. #include "ipc/ipc_perftest_messages.h"
  18. #include "ipc/ipc_perftest_util.h"
  19. #include "ipc/ipc_sync_channel.h"
  20. #include "ipc/ipc_test.mojom.h"
  21. #include "ipc/ipc_test_base.h"
  22. #include "mojo/core/test/mojo_test_base.h"
  23. #include "mojo/core/test/multiprocess_test_helper.h"
  24. #include "mojo/public/cpp/bindings/pending_remote.h"
  25. #include "mojo/public/cpp/bindings/remote.h"
  26. #include "mojo/public/cpp/system/message_pipe.h"
  27. namespace IPC {
  28. namespace {
  29. struct TestParams {
  30. TestParams() = default;
  31. TestParams(size_t in_message_size,
  32. size_t in_frames_per_second,
  33. size_t in_messages_per_frame,
  34. size_t in_duration_in_seconds)
  35. : message_size(in_message_size),
  36. frames_per_second(in_frames_per_second),
  37. messages_per_frame(in_messages_per_frame),
  38. duration_in_seconds(in_duration_in_seconds) {}
  39. size_t message_size;
  40. size_t frames_per_second;
  41. size_t messages_per_frame;
  42. size_t duration_in_seconds;
  43. };
  44. std::vector<TestParams> GetDefaultTestParams() {
  45. std::vector<TestParams> list;
  46. list.push_back({144, 20, 10, 10});
  47. list.push_back({144, 60, 10, 10});
  48. return list;
  49. }
  50. std::string GetLogTitle(const std::string& label, const TestParams& params) {
  51. return base::StringPrintf(
  52. "%s_MsgSize_%zu_FrmPerSec_%zu_MsgPerFrm_%zu", label.c_str(),
  53. params.message_size, params.frames_per_second, params.messages_per_frame);
  54. }
  55. base::TimeDelta GetFrameTime(size_t frames_per_second) {
  56. return base::Seconds(1.0 / frames_per_second);
  57. }
  58. class PerfCpuLogger {
  59. public:
  60. explicit PerfCpuLogger(base::StringPiece test_name)
  61. : test_name_(test_name),
  62. process_metrics_(base::ProcessMetrics::CreateCurrentProcessMetrics()) {
  63. // Query the CPU usage once to start the recording interval.
  64. const double inital_cpu_usage =
  65. process_metrics_->GetPlatformIndependentCPUUsage();
  66. // This should have been the first call so the reported cpu usage should be
  67. // exactly zero.
  68. DCHECK_EQ(inital_cpu_usage, 0.0);
  69. }
  70. PerfCpuLogger(const PerfCpuLogger&) = delete;
  71. PerfCpuLogger& operator=(const PerfCpuLogger&) = delete;
  72. ~PerfCpuLogger() {
  73. double result = process_metrics_->GetPlatformIndependentCPUUsage();
  74. base::LogPerfResult(test_name_.c_str(), result, "%");
  75. }
  76. private:
  77. std::string test_name_;
  78. std::unique_ptr<base::ProcessMetrics> process_metrics_;
  79. };
  80. MULTIPROCESS_TEST_MAIN(MojoPerfTestClientTestChildMain) {
  81. MojoPerfTestClient client;
  82. int rv = mojo::core::test::MultiprocessTestHelper::RunClientMain(
  83. base::BindOnce(&MojoPerfTestClient::Run, base::Unretained(&client)),
  84. true /* pass_pipe_ownership_to_main */);
  85. base::RunLoop run_loop;
  86. run_loop.RunUntilIdle();
  87. return rv;
  88. }
  89. class ChannelSteadyPingPongListener : public Listener {
  90. public:
  91. ChannelSteadyPingPongListener() = default;
  92. ~ChannelSteadyPingPongListener() override = default;
  93. void Init(Sender* sender) {
  94. DCHECK(!sender_);
  95. sender_ = sender;
  96. }
  97. void SetTestParams(const TestParams& params,
  98. const std::string& label,
  99. bool sync,
  100. base::OnceClosure quit_closure) {
  101. params_ = params;
  102. label_ = label;
  103. sync_ = sync;
  104. quit_closure_ = std::move(quit_closure);
  105. payload_ = std::string(params.message_size, 'a');
  106. }
  107. bool OnMessageReceived(const Message& message) override {
  108. CHECK(sender_);
  109. bool handled = true;
  110. IPC_BEGIN_MESSAGE_MAP(ChannelSteadyPingPongListener, message)
  111. IPC_MESSAGE_HANDLER(TestMsg_Hello, OnHello)
  112. IPC_MESSAGE_HANDLER(TestMsg_Ping, OnPing)
  113. IPC_MESSAGE_UNHANDLED(handled = false)
  114. IPC_END_MESSAGE_MAP()
  115. return handled;
  116. }
  117. void OnHello() {
  118. cpu_logger_ = std::make_unique<PerfCpuLogger>(GetLogTitle(label_, params_));
  119. frame_count_down_ = params_.frames_per_second * params_.duration_in_seconds;
  120. timer_.Start(FROM_HERE, GetFrameTime(params_.frames_per_second), this,
  121. &ChannelSteadyPingPongListener::StartPingPong);
  122. }
  123. void StartPingPong() {
  124. if (sync_) {
  125. base::TimeTicks before = base::TimeTicks::Now();
  126. for (count_down_ = params_.messages_per_frame; count_down_ > 0;
  127. --count_down_) {
  128. std::string response;
  129. sender_->Send(new TestMsg_SyncPing(payload_, &response));
  130. DCHECK_EQ(response, payload_);
  131. }
  132. if (base::TimeTicks::Now() - before >
  133. GetFrameTime(params_.frames_per_second)) {
  134. LOG(ERROR) << "Frame " << frame_count_down_
  135. << " wasn't able to complete on time!";
  136. }
  137. CHECK_GT(frame_count_down_, 0);
  138. frame_count_down_--;
  139. if (frame_count_down_ == 0)
  140. StopPingPong();
  141. } else {
  142. if (count_down_ != 0) {
  143. LOG(ERROR) << "Frame " << frame_count_down_
  144. << " wasn't able to complete on time!";
  145. } else {
  146. SendPong();
  147. }
  148. count_down_ = params_.messages_per_frame;
  149. }
  150. }
  151. void StopPingPong() {
  152. cpu_logger_.reset();
  153. timer_.AbandonAndStop();
  154. std::move(quit_closure_).Run();
  155. }
  156. void OnPing(const std::string& payload) {
  157. // Include message deserialization in latency.
  158. DCHECK_EQ(payload_.size(), payload.size());
  159. CHECK_GT(count_down_, 0);
  160. count_down_--;
  161. if (count_down_ > 0) {
  162. SendPong();
  163. } else {
  164. CHECK_GT(frame_count_down_, 0);
  165. frame_count_down_--;
  166. if (frame_count_down_ == 0)
  167. StopPingPong();
  168. }
  169. }
  170. void SendPong() { sender_->Send(new TestMsg_Ping(payload_)); }
  171. private:
  172. raw_ptr<Sender> sender_ = nullptr;
  173. TestParams params_;
  174. std::string payload_;
  175. std::string label_;
  176. bool sync_ = false;
  177. int count_down_ = 0;
  178. int frame_count_down_ = 0;
  179. base::RepeatingTimer timer_;
  180. std::unique_ptr<PerfCpuLogger> cpu_logger_;
  181. base::OnceClosure quit_closure_;
  182. };
  183. class ChannelSteadyPingPongTest : public IPCChannelMojoTestBase {
  184. public:
  185. ChannelSteadyPingPongTest() = default;
  186. ~ChannelSteadyPingPongTest() override = default;
  187. void RunPingPongServer(const std::string& label, bool sync) {
  188. Init("MojoPerfTestClient");
  189. // Set up IPC channel and start client.
  190. ChannelSteadyPingPongListener listener;
  191. std::unique_ptr<ChannelProxy> channel_proxy;
  192. std::unique_ptr<base::WaitableEvent> shutdown_event;
  193. if (sync) {
  194. shutdown_event = std::make_unique<base::WaitableEvent>(
  195. base::WaitableEvent::ResetPolicy::MANUAL,
  196. base::WaitableEvent::InitialState::NOT_SIGNALED);
  197. channel_proxy = IPC::SyncChannel::Create(
  198. TakeHandle().release(), IPC::Channel::MODE_SERVER, &listener,
  199. GetIOThreadTaskRunner(), base::ThreadTaskRunnerHandle::Get(), false,
  200. shutdown_event.get());
  201. } else {
  202. channel_proxy = IPC::ChannelProxy::Create(
  203. TakeHandle().release(), IPC::Channel::MODE_SERVER, &listener,
  204. GetIOThreadTaskRunner(), base::ThreadTaskRunnerHandle::Get());
  205. }
  206. listener.Init(channel_proxy.get());
  207. LockThreadAffinity thread_locker(kSharedCore);
  208. std::vector<TestParams> params_list = GetDefaultTestParams();
  209. for (const auto& params : params_list) {
  210. base::RunLoop run_loop;
  211. listener.SetTestParams(params, label, sync,
  212. run_loop.QuitWhenIdleClosure());
  213. // This initial message will kick-start the ping-pong of messages.
  214. channel_proxy->Send(new TestMsg_Hello);
  215. run_loop.Run();
  216. }
  217. // Send quit message.
  218. channel_proxy->Send(new TestMsg_Quit);
  219. EXPECT_TRUE(WaitForClientShutdown());
  220. channel_proxy.reset();
  221. }
  222. };
  223. TEST_F(ChannelSteadyPingPongTest, AsyncPingPong) {
  224. RunPingPongServer("IPC_CPU_Async", false);
  225. }
  226. TEST_F(ChannelSteadyPingPongTest, SyncPingPong) {
  227. RunPingPongServer("IPC_CPU_Sync", true);
  228. }
  229. class MojoSteadyPingPongTest : public mojo::core::test::MojoTestBase {
  230. public:
  231. MojoSteadyPingPongTest() = default;
  232. MojoSteadyPingPongTest(const MojoSteadyPingPongTest&) = delete;
  233. MojoSteadyPingPongTest& operator=(const MojoSteadyPingPongTest&) = delete;
  234. protected:
  235. void RunPingPongServer(MojoHandle mp, const std::string& label, bool sync) {
  236. label_ = label;
  237. sync_ = sync;
  238. mojo::MessagePipeHandle mp_handle(mp);
  239. mojo::ScopedMessagePipeHandle scoped_mp(mp_handle);
  240. ping_receiver_.Bind(
  241. mojo::PendingRemote<IPC::mojom::Reflector>(std::move(scoped_mp), 0u));
  242. LockThreadAffinity thread_locker(kSharedCore);
  243. std::vector<TestParams> params_list = GetDefaultTestParams();
  244. for (const auto& params : params_list) {
  245. params_ = params;
  246. payload_ = std::string(params.message_size, 'a');
  247. ping_receiver_->Ping("hello",
  248. base::BindOnce(&MojoSteadyPingPongTest::OnHello,
  249. base::Unretained(this)));
  250. base::RunLoop run_loop;
  251. quit_closure_ = run_loop.QuitWhenIdleClosure();
  252. run_loop.Run();
  253. }
  254. ping_receiver_->Quit();
  255. std::ignore = ping_receiver_.Unbind().PassPipe().release();
  256. }
  257. void OnHello(const std::string& value) {
  258. cpu_logger_ = std::make_unique<PerfCpuLogger>(GetLogTitle(label_, params_));
  259. frame_count_down_ = params_.frames_per_second * params_.duration_in_seconds;
  260. timer_.Start(FROM_HERE, GetFrameTime(params_.frames_per_second), this,
  261. &MojoSteadyPingPongTest::StartPingPong);
  262. }
  263. void StartPingPong() {
  264. if (sync_) {
  265. base::TimeTicks before = base::TimeTicks::Now();
  266. for (count_down_ = params_.messages_per_frame; count_down_ > 0;
  267. --count_down_) {
  268. std::string response;
  269. ping_receiver_->SyncPing(payload_, &response);
  270. DCHECK_EQ(response, payload_);
  271. }
  272. if (base::TimeTicks::Now() - before >
  273. GetFrameTime(params_.frames_per_second)) {
  274. LOG(ERROR) << "Frame " << frame_count_down_
  275. << " wasn't able to complete on time!";
  276. }
  277. CHECK_GT(frame_count_down_, 0);
  278. frame_count_down_--;
  279. if (frame_count_down_ == 0)
  280. StopPingPong();
  281. } else {
  282. if (count_down_ != 0) {
  283. LOG(ERROR) << "Frame " << frame_count_down_
  284. << " wasn't able to complete on time!";
  285. } else {
  286. SendPing();
  287. }
  288. count_down_ = params_.messages_per_frame;
  289. }
  290. }
  291. void StopPingPong() {
  292. cpu_logger_.reset();
  293. timer_.AbandonAndStop();
  294. std::move(quit_closure_).Run();
  295. }
  296. void OnPong(const std::string& value) {
  297. // Include message deserialization in latency.
  298. DCHECK_EQ(payload_.size(), value.size());
  299. CHECK_GT(count_down_, 0);
  300. count_down_--;
  301. if (count_down_ > 0) {
  302. SendPing();
  303. } else {
  304. CHECK_GT(frame_count_down_, 0);
  305. frame_count_down_--;
  306. if (frame_count_down_ == 0)
  307. StopPingPong();
  308. }
  309. }
  310. void SendPing() {
  311. ping_receiver_->Ping(payload_,
  312. base::BindOnce(&MojoSteadyPingPongTest::OnPong,
  313. base::Unretained(this)));
  314. }
  315. static int RunPingPongClient(MojoHandle mp) {
  316. mojo::MessagePipeHandle mp_handle(mp);
  317. mojo::ScopedMessagePipeHandle scoped_mp(mp_handle);
  318. LockThreadAffinity thread_locker(kSharedCore);
  319. base::RunLoop run_loop;
  320. ReflectorImpl impl(std::move(scoped_mp), run_loop.QuitWhenIdleClosure());
  321. run_loop.Run();
  322. return 0;
  323. }
  324. private:
  325. TestParams params_;
  326. std::string payload_;
  327. std::string label_;
  328. bool sync_ = false;
  329. mojo::Remote<IPC::mojom::Reflector> ping_receiver_;
  330. int count_down_ = 0;
  331. int frame_count_down_ = 0;
  332. base::RepeatingTimer timer_;
  333. std::unique_ptr<PerfCpuLogger> cpu_logger_;
  334. base::OnceClosure quit_closure_;
  335. };
  336. DEFINE_TEST_CLIENT_WITH_PIPE(PingPongClient, MojoSteadyPingPongTest, h) {
  337. base::test::SingleThreadTaskEnvironment task_environment;
  338. return RunPingPongClient(h);
  339. }
  340. // Similar to ChannelSteadyPingPongTest above, but uses a Mojo interface
  341. // instead of raw IPC::Messages.
  342. TEST_F(MojoSteadyPingPongTest, AsyncPingPong) {
  343. RunTestClient("PingPongClient", [&](MojoHandle h) {
  344. base::test::SingleThreadTaskEnvironment task_environment;
  345. RunPingPongServer(h, "Mojo_CPU_Async", false);
  346. });
  347. }
  348. TEST_F(MojoSteadyPingPongTest, SyncPingPong) {
  349. RunTestClient("PingPongClient", [&](MojoHandle h) {
  350. base::test::SingleThreadTaskEnvironment task_environment;
  351. RunPingPongServer(h, "Mojo_CPU_Sync", true);
  352. });
  353. }
  354. } // namespace
  355. } // namespace IPC