Mir

Merge lp:~kdub/mir/fix-1578159 into lp:mir

Proposed by Kevin DuBois
Status: Merged
Approved by: Daniel van Vugt
Approved revision: no longer in the source branch.
Merged at revision: 3497
Proposed branch: lp:~kdub/mir/fix-1578159
Merge into: lp:mir
Diff against target: 149 lines (+68/-6)
4 files modified
src/client/buffer_vault.cpp (+8/-3)
src/client/buffer_vault.h (+1/-0)
tests/integration-tests/test_buffer_scheduling.cpp (+38/-3)
tests/unit-tests/client/test_buffer_vault.cpp (+21/-0)
To merge this branch: bzr merge lp:~kdub/mir/fix-1578159
Reviewer Review Type Date Requested Status
Daniel van Vugt Abstain
Cemil Azizoglu (community) Approve
Alan Griffiths Approve
Mir CI Bot continuous-integration Approve
Review via email: mp+293790@code.launchpad.net

Commit message

client: fix lp: #1578159 by preferring buffers that returned to the client earlier, as opposed to preferring the last-returned buffer.

fixes: LP: #1578159

Note on test change to "queue_size_scales_with_client_performance":
The nbs code, although now cycling through the 3 buffers, still will perform as double buffered, as it always has a spare buffer to work with.

Description of the change

client: fix lp: #1578159 by preferring buffers that returned to the client earlier, as opposed to preferring the last-returned buffer.

fixes: LP: #1578159

Note on test change to "queue_size_scales_with_client_performance":
The nbs code, although now cycling through the 3 buffers, still will perform as double buffered, as it always has a spare buffer to work with.

To post a comment you must log in.
Revision history for this message
Mir CI Bot (mir-ci-bot) wrote :

PASSED: Continuous integration, rev:3497
https://mir-jenkins.ubuntu.com/job/mir-ci/942/
Executed test runs:
    SUCCESS: https://mir-jenkins.ubuntu.com/job/build-mir/1004
    SUCCESS: https://mir-jenkins.ubuntu.com/job/build-0-fetch/1050
    SUCCESS: https://mir-jenkins.ubuntu.com/job/build-1-sourcepkg/release=vivid+overlay/1041
    SUCCESS: https://mir-jenkins.ubuntu.com/job/build-1-sourcepkg/release=xenial/1041
    SUCCESS: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=amd64,compiler=clang,platform=mesa,release=vivid+overlay/1014
        deb: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=amd64,compiler=clang,platform=mesa,release=vivid+overlay/1014/artifact/output/*zip*/output.zip
    SUCCESS: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=amd64,compiler=gcc,platform=mesa,release=xenial/1014
        deb: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=amd64,compiler=gcc,platform=mesa,release=xenial/1014/artifact/output/*zip*/output.zip
    SUCCESS: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=cross-armhf,compiler=gcc,platform=android,release=vivid+overlay/1014
        deb: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=cross-armhf,compiler=gcc,platform=android,release=vivid+overlay/1014/artifact/output/*zip*/output.zip
    SUCCESS: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=i386,compiler=gcc,platform=android,release=vivid+overlay/1014
        deb: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=i386,compiler=gcc,platform=android,release=vivid+overlay/1014/artifact/output/*zip*/output.zip
    SUCCESS: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=i386,compiler=gcc,platform=mesa,release=xenial/1014
        deb: https://mir-jenkins.ubuntu.com/job/build-2-binpkg-mir/arch=i386,compiler=gcc,platform=mesa,release=xenial/1014/artifact/output/*zip*/output.zip

Click here to trigger a rebuild:
https://mir-jenkins.ubuntu.com/job/mir-ci/942/rebuild

review: Approve (continuous-integration)
Revision history for this message
Alan Griffiths (alan-griffiths) wrote :

I can't help feeling that the double find_if() search indicates we're using the wrong data structure. But OK.

review: Approve
Revision history for this message
Cemil Azizoglu (cemil-azizoglu) wrote :

Ok.

Nit: These lines are not needed.

147 +
148 +

review: Approve
Revision history for this message
Kevin DuBois (kdub) wrote :

> I can't help feeling that the double find_if() search indicates we're using
> the wrong data structure. But OK.

Yeah, a queue would be better

Revision history for this message
Kevin DuBois (kdub) wrote :

still testing cross-device, probably should have put this as wip when proposed

Revision history for this message
Daniel van Vugt (vanvugt) wrote :

Sounds like the right fix. If we were preferring the latest buffer returned then we were accidentally double buffering instead of triple. Which would quantize to half frame rate more easily.

What concerns me though is that you've broken one aspect of the old test cases. In that you no longer expect minimal buffer allocation [1]:

88 - EXPECT_THAT(unique_ids_in(log), Eq(2));
89 + if (std::get<1>(GetParam()) == TestType::SubmitSemantics)
90 + EXPECT_THAT(log, NeverBlocks());
91 + if (std::get<1>(GetParam()) == TestType::ExchangeSemantics)
92 + EXPECT_THAT(unique_ids_in(log), Eq(2));

[1] https://docs.google.com/spreadsheets/d/1qf3IssN_sujygMPK2sTX2AUhGiDUBF3TMYYcSRSaSy0/pubhtml

That's a slight regression compared to BufferQueue. Although BufferQueue did not do it well to start with.

review: Abstain
Revision history for this message
Daniel van Vugt (vanvugt) wrote :

Come to think of it... Instead of breaking the tests, please just disable them and comment "TODO: Reintroduce efficient allocation later".

It's not a regression test if you have different expectations for the old and new ways.

review: Needs Fixing
Revision history for this message
Kevin DuBois (kdub) wrote :

> Sounds like the right fix. If we were preferring the latest buffer returned
> then we were accidentally double buffering instead of triple. Which would
> quantize to half frame rate more easily.

Right, but this is on design... it will scale us to double buffering when the 3rd isn't needed. Alternating which buffer is the spare buffer still has the benefits of double buffering, although there are 3 buffers involved.

> What concerns me though is that you've broken one aspect of the old test
> cases. In that you no longer expect minimal buffer allocation [1]:

Is this what the test is checking for though? The nbs stuff still has 3 buffers allocated. The test is checking the output for profiles consistent with double buffering. The change to the test is to accommodate the alternation of the 3rd buffer in and out to improve android performance.

Revision history for this message
Kevin DuBois (kdub) wrote :

tested nbs vs obs on phone stack on arale, flo, krillin, and should be at parity. Still digging though on what seems to be an unrelated-to-this-mp issue

Revision history for this message
Daniel van Vugt (vanvugt) wrote :

You're right the test is dual-purpose. Although I can't seem to find another test that covers the number of buffers touched allocation-efficiency issue. So I would prefer the test retained its dual purposes.

That said, it's not a major problem or entirely new if we have 3 buffers allocated when only 2 are in use. That's a 'regression' we could happily live with indefinitely.

review: Abstain

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'src/client/buffer_vault.cpp'
2--- src/client/buffer_vault.cpp 2016-05-03 09:49:54 +0000
3+++ src/client/buffer_vault.cpp 2016-05-04 14:48:14 +0000
4@@ -129,8 +129,13 @@
5 auto it = std::find_if(buffers.begin(), buffers.end(),
6 [this](std::pair<int, Owner> const& entry) {
7 return ((entry.second == Owner::Self) &&
8- (checked_buffer_from_map(entry.first)->size() == size));
9- });
10+ (checked_buffer_from_map(entry.first)->size() == size) &&
11+ (entry.first != last_received_id)); });
12+ if (it == buffers.end())
13+ it = std::find_if(buffers.begin(), buffers.end(),
14+ [this](std::pair<int, Owner> const& entry) {
15+ return ((entry.second == Owner::Self) &&
16+ (checked_buffer_from_map(entry.first)->size() == size)); });
17
18 auto future = promise.get_future();
19 if (it != buffers.end())
20@@ -172,7 +177,7 @@
21 void mcl::BufferVault::wire_transfer_inbound(int buffer_id)
22 {
23 std::unique_lock<std::mutex> lk(mutex);
24-
25+ last_received_id = buffer_id;
26 auto buffer = checked_buffer_from_map(buffer_id);
27 auto inbound_size = buffer->size();
28 auto it = buffers.find(buffer_id);
29
30=== modified file 'src/client/buffer_vault.h'
31--- src/client/buffer_vault.h 2016-05-03 09:49:54 +0000
32+++ src/client/buffer_vault.h 2016-05-04 14:48:14 +0000
33@@ -96,6 +96,7 @@
34 size_t current_buffer_count;
35 size_t needed_buffer_count;
36 size_t const initial_buffer_count;
37+ int last_received_id = 0;
38 };
39 }
40 }
41
42=== modified file 'tests/integration-tests/test_buffer_scheduling.cpp'
43--- tests/integration-tests/test_buffer_scheduling.cpp 2016-05-03 09:49:54 +0000
44+++ tests/integration-tests/test_buffer_scheduling.cpp 2016-05-04 14:48:14 +0000
45@@ -144,7 +144,7 @@
46 }
47 else
48 {
49- entries.emplace_back(BufferEntry{mg::BufferID{2}, 0u, Access::blocked});
50+ entries.emplace_back(BufferEntry{mg::BufferID{INT_MAX}, 0u, Access::blocked});
51 }
52 }
53
54@@ -565,6 +565,15 @@
55 return std::distance(log.begin(), it);
56 }
57
58+MATCHER(NeverBlocks, "")
59+{
60+ bool never_blocks = true;
61+ for(auto& e : arg)
62+ never_blocks &= (e.blockage == Access::unblocked);
63+ return never_blocks;
64+}
65+
66+
67 //test infrastructure
68 struct BufferScheduling : public Test, ::testing::WithParamInterface<std::tuple<int, TestType>>
69 {
70@@ -1454,7 +1463,12 @@
71 auto log = producer->production_log();
72 ASSERT_THAT(log.size(), Gt(discard)); // avoid the below erase crashing
73 log.erase(log.begin(), log.begin() + discard);
74- EXPECT_THAT(unique_ids_in(log), Eq(2));
75+
76+ if (std::get<1>(GetParam()) == TestType::SubmitSemantics)
77+ EXPECT_THAT(log, NeverBlocks());
78+ if (std::get<1>(GetParam()) == TestType::ExchangeSemantics)
79+ EXPECT_THAT(unique_ids_in(log), Eq(2));
80+
81 producer->reset_log();
82
83 //put server-side pressure on the buffer count
84@@ -1485,7 +1499,10 @@
85 // Expect double-buffers as the steady state for fast clients
86 log = producer->production_log();
87 log.erase(log.begin(), log.begin() + discard);
88- EXPECT_THAT(unique_ids_in(log), Eq(2));
89+ if (std::get<1>(GetParam()) == TestType::SubmitSemantics)
90+ EXPECT_THAT(log, NeverBlocks());
91+ if (std::get<1>(GetParam()) == TestType::ExchangeSemantics)
92+ EXPECT_THAT(unique_ids_in(log), Eq(2));
93 }
94
95 //NOTE: compositors need 2 buffers in overlay/bypass cases, as they
96@@ -1532,6 +1549,24 @@
97 EXPECT_THAT(snaps, Each(production_log.back().id));
98 }
99
100+//LP: #1578159
101+//If given the choice best to prefer buffers that the compositor used furthest in the past
102+//so that we avert any waits on synchronization internal to the buffers or platform.
103+TEST_P(WithThreeOrMoreBuffers, prefers_fifo_ordering_when_distributing_buffers_to_driver)
104+{
105+ producer->produce();
106+ producer->produce();
107+ consumer->consume();
108+ consumer->consume();
109+ producer->produce();
110+
111+ auto production_log = producer->production_log();
112+ auto consumption_log = consumer->consumption_log();
113+ ASSERT_THAT(production_log, Not(IsEmpty()));
114+ ASSERT_THAT(consumption_log, Not(IsEmpty()));
115+ EXPECT_THAT(production_log.back().id, Ne(consumption_log.front().id));
116+}
117+
118 int const max_buffers_to_test{5};
119 INSTANTIATE_TEST_CASE_P(
120 BufferScheduling,
121
122=== modified file 'tests/unit-tests/client/test_buffer_vault.cpp'
123--- tests/unit-tests/client/test_buffer_vault.cpp 2016-05-03 09:49:54 +0000
124+++ tests/unit-tests/client/test_buffer_vault.cpp 2016-05-04 14:48:14 +0000
125@@ -554,3 +554,24 @@
126 vault.wire_transfer_inbound(package2.buffer_id());
127 Mock::VerifyAndClearExpectations(&mock_requests);
128 }
129+
130+//LP: #1578159
131+TEST_F(StartedBufferVault, prefers_buffers_returned_further_in_the_past)
132+{
133+ auto buffer = vault.withdraw().get();
134+ vault.deposit(buffer);
135+ vault.wire_transfer_outbound(buffer);
136+ auto first_id = buffer->rpc_id();
137+
138+ buffer = vault.withdraw().get();
139+ vault.deposit(buffer);
140+ vault.wire_transfer_outbound(buffer);
141+ auto second_id = buffer->rpc_id();
142+
143+ vault.wire_transfer_inbound(second_id);
144+ vault.wire_transfer_inbound(first_id);
145+
146+ EXPECT_THAT(vault.withdraw().get()->rpc_id(), Ne(first_id));
147+
148+
149+}

Subscribers

People subscribed via source and target branches