The C based gRPC (C++, Python, Ruby, Objective-C, PHP, C#) https://grpc.io/
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

587 lines
16 KiB

// Copyright 2023 gRPC authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include "src/core/lib/promise/party.h"
#include <stdio.h>
#include <algorithm>
#include <atomic>
#include <memory>
#include <thread>
#include <vector>
#include "absl/base/thread_annotations.h"
#include "absl/log/log.h"
#include "gtest/gtest.h"
#include <grpc/event_engine/event_engine.h>
#include <grpc/event_engine/memory_allocator.h>
#include <grpc/grpc.h>
#include "src/core/lib/event_engine/default_event_engine.h"
#include "src/core/lib/event_engine/event_engine_context.h"
#include "src/core/lib/gprpp/notification.h"
#include "src/core/lib/gprpp/ref_counted_ptr.h"
#include "src/core/lib/gprpp/sync.h"
#include "src/core/lib/gprpp/time.h"
#include "src/core/lib/iomgr/exec_ctx.h"
#include "src/core/lib/promise/context.h"
#include "src/core/lib/promise/inter_activity_latch.h"
#include "src/core/lib/promise/poll.h"
#include "src/core/lib/promise/seq.h"
#include "src/core/lib/promise/sleep.h"
#include "src/core/lib/resource_quota/arena.h"
#include "src/core/lib/resource_quota/memory_quota.h"
#include "src/core/lib/resource_quota/resource_quota.h"
namespace grpc_core {
///////////////////////////////////////////////////////////////////////////////
// PartyTest
class PartyTest : public ::testing::Test {
protected:
RefCountedPtr<Party> MakeParty() {
auto arena = SimpleArenaAllocator()->MakeArena();
arena->SetContext<grpc_event_engine::experimental::EventEngine>(
event_engine_.get());
return Party::Make(std::move(arena));
}
private:
std::shared_ptr<grpc_event_engine::experimental::EventEngine> event_engine_ =
grpc_event_engine::experimental::GetDefaultEventEngine();
};
TEST_F(PartyTest, Noop) { auto party = MakeParty(); }
TEST_F(PartyTest, CanSpawnAndRun) {
auto party = MakeParty();
Notification n;
party->Spawn(
"TestSpawn",
[i = 10]() mutable -> Poll<int> {
EXPECT_GT(i, 0);
GetContext<Activity>()->ForceImmediateRepoll();
--i;
if (i == 0) return 42;
return Pending{};
},
[&n](int x) {
EXPECT_EQ(x, 42);
n.Notify();
});
n.WaitForNotification();
}
TEST_F(PartyTest, CanSpawnWaitableAndRun) {
auto party1 = MakeParty();
auto party2 = MakeParty();
Notification n;
InterActivityLatch<void> done;
// Spawn a task on party1 that will wait for a task on party2.
// The party2 task will wait on the latch `done`.
party1->Spawn(
"party1_main",
[&party2, &done]() {
return party2->SpawnWaitable("party2_main",
[&done]() { return done.Wait(); });
},
[&n](Empty) { n.Notify(); });
ASSERT_FALSE(n.HasBeenNotified());
party1->Spawn(
"party1_notify_latch",
[&done]() {
done.Set();
return Empty{};
},
[](Empty) {});
n.WaitForNotification();
}
TEST_F(PartyTest, CanSpawnFromSpawn) {
auto party = MakeParty();
Notification n1;
Notification n2;
party->Spawn(
"TestSpawn",
[party, &n2]() -> Poll<int> {
party->Spawn(
"TestSpawnInner",
[i = 10]() mutable -> Poll<int> {
GetContext<Activity>()->ForceImmediateRepoll();
--i;
if (i == 0) return 42;
return Pending{};
},
[&n2](int x) {
EXPECT_EQ(x, 42);
n2.Notify();
});
return 1234;
},
[&n1](int x) {
EXPECT_EQ(x, 1234);
n1.Notify();
});
n1.WaitForNotification();
n2.WaitForNotification();
}
TEST_F(PartyTest, CanWakeupWithOwningWaker) {
auto party = MakeParty();
Notification n[10];
Notification complete;
Waker waker;
party->Spawn(
"TestSpawn",
[i = 0, &waker, &n]() mutable -> Poll<int> {
waker = GetContext<Activity>()->MakeOwningWaker();
n[i].Notify();
i++;
if (i == 10) return 42;
return Pending{};
},
[&complete](int x) {
EXPECT_EQ(x, 42);
complete.Notify();
});
for (int i = 0; i < 10; i++) {
n[i].WaitForNotification();
waker.Wakeup();
}
complete.WaitForNotification();
}
TEST_F(PartyTest, CanWakeupWithNonOwningWaker) {
auto party = MakeParty();
Notification n[10];
Notification complete;
Waker waker;
party->Spawn(
"TestSpawn",
[i = 10, &waker, &n]() mutable -> Poll<int> {
waker = GetContext<Activity>()->MakeNonOwningWaker();
--i;
n[9 - i].Notify();
if (i == 0) return 42;
return Pending{};
},
[&complete](int x) {
EXPECT_EQ(x, 42);
complete.Notify();
});
for (int i = 0; i < 9; i++) {
n[i].WaitForNotification();
EXPECT_FALSE(n[i + 1].HasBeenNotified());
waker.Wakeup();
}
complete.WaitForNotification();
}
TEST_F(PartyTest, CanWakeupWithNonOwningWakerAfterOrphaning) {
auto party = MakeParty();
Notification set_waker;
Waker waker;
party->Spawn(
"TestSpawn",
[&waker, &set_waker]() mutable -> Poll<int> {
EXPECT_FALSE(set_waker.HasBeenNotified());
waker = GetContext<Activity>()->MakeNonOwningWaker();
set_waker.Notify();
return Pending{};
},
[](int) { Crash("unreachable"); });
set_waker.WaitForNotification();
party.reset();
EXPECT_FALSE(waker.is_unwakeable());
waker.Wakeup();
EXPECT_TRUE(waker.is_unwakeable());
}
TEST_F(PartyTest, CanDropNonOwningWakeAfterOrphaning) {
auto party = MakeParty();
Notification set_waker;
std::unique_ptr<Waker> waker;
party->Spawn(
"TestSpawn",
[&waker, &set_waker]() mutable -> Poll<int> {
EXPECT_FALSE(set_waker.HasBeenNotified());
waker = std::make_unique<Waker>(
GetContext<Activity>()->MakeNonOwningWaker());
set_waker.Notify();
return Pending{};
},
[](int) { Crash("unreachable"); });
set_waker.WaitForNotification();
party.reset();
EXPECT_NE(waker, nullptr);
waker.reset();
}
TEST_F(PartyTest, CanWakeupNonOwningOrphanedWakerWithNoEffect) {
auto party = MakeParty();
Notification set_waker;
Waker waker;
party->Spawn(
"TestSpawn",
[&waker, &set_waker]() mutable -> Poll<int> {
EXPECT_FALSE(set_waker.HasBeenNotified());
waker = GetContext<Activity>()->MakeNonOwningWaker();
set_waker.Notify();
return Pending{};
},
[](int) { Crash("unreachable"); });
set_waker.WaitForNotification();
EXPECT_FALSE(waker.is_unwakeable());
party.reset();
waker.Wakeup();
EXPECT_TRUE(waker.is_unwakeable());
}
TEST_F(PartyTest, CanBulkSpawn) {
auto party = MakeParty();
Notification n1;
Notification n2;
{
Party::BulkSpawner spawner(party.get());
spawner.Spawn(
"spawn1", []() { return Empty{}; }, [&n1](Empty) { n1.Notify(); });
spawner.Spawn(
"spawn2", []() { return Empty{}; }, [&n2](Empty) { n2.Notify(); });
for (int i = 0; i < 5000; i++) {
EXPECT_FALSE(n1.HasBeenNotified());
EXPECT_FALSE(n2.HasBeenNotified());
}
}
n1.WaitForNotification();
n2.WaitForNotification();
}
TEST_F(PartyTest, ThreadStressTest) {
auto party = MakeParty();
std::vector<std::thread> threads;
threads.reserve(8);
for (int i = 0; i < 8; i++) {
threads.emplace_back([party]() {
for (int i = 0; i < 100; i++) {
ExecCtx ctx; // needed for Sleep
Notification promise_complete;
party->Spawn("TestSpawn",
Seq(Sleep(Timestamp::Now() + Duration::Milliseconds(10)),
[]() -> Poll<int> { return 42; }),
[&promise_complete](int i) {
EXPECT_EQ(i, 42);
promise_complete.Notify();
});
promise_complete.WaitForNotification();
}
});
}
for (auto& thread : threads) {
thread.join();
}
}
class PromiseNotification {
public:
explicit PromiseNotification(bool owning_waker)
: owning_waker_(owning_waker) {}
auto Wait() {
return [this]() -> Poll<int> {
MutexLock lock(&mu_);
if (done_) return 42;
if (!polled_) {
if (owning_waker_) {
waker_ = GetContext<Activity>()->MakeOwningWaker();
} else {
waker_ = GetContext<Activity>()->MakeNonOwningWaker();
}
polled_ = true;
}
return Pending{};
};
}
void Notify() {
Waker waker;
{
MutexLock lock(&mu_);
done_ = true;
waker = std::move(waker_);
}
waker.Wakeup();
}
void NotifyUnderLock() {
MutexLock lock(&mu_);
done_ = true;
waker_.WakeupAsync();
}
private:
Mutex mu_;
const bool owning_waker_;
bool done_ ABSL_GUARDED_BY(mu_) = false;
bool polled_ ABSL_GUARDED_BY(mu_) = false;
Waker waker_ ABSL_GUARDED_BY(mu_);
};
TEST_F(PartyTest, ThreadStressTestWithOwningWaker) {
auto party = MakeParty();
std::vector<std::thread> threads;
threads.reserve(8);
for (int i = 0; i < 8; i++) {
threads.emplace_back([party]() {
for (int i = 0; i < 100; i++) {
ExecCtx ctx; // needed for Sleep
PromiseNotification promise_start(true);
Notification promise_complete;
party->Spawn("TestSpawn",
Seq(promise_start.Wait(),
Sleep(Timestamp::Now() + Duration::Milliseconds(10)),
[]() -> Poll<int> { return 42; }),
[&promise_complete](int i) {
EXPECT_EQ(i, 42);
promise_complete.Notify();
});
promise_start.Notify();
promise_complete.WaitForNotification();
}
});
}
for (auto& thread : threads) {
thread.join();
}
}
TEST_F(PartyTest, ThreadStressTestWithOwningWakerHoldingLock) {
auto party = MakeParty();
std::vector<std::thread> threads;
threads.reserve(8);
for (int i = 0; i < 8; i++) {
threads.emplace_back([party]() {
for (int i = 0; i < 100; i++) {
ExecCtx ctx; // needed for Sleep
PromiseNotification promise_start(true);
Notification promise_complete;
party->Spawn("TestSpawn",
Seq(promise_start.Wait(),
Sleep(Timestamp::Now() + Duration::Milliseconds(10)),
[]() -> Poll<int> { return 42; }),
[&promise_complete](int i) {
EXPECT_EQ(i, 42);
promise_complete.Notify();
});
promise_start.NotifyUnderLock();
promise_complete.WaitForNotification();
}
});
}
for (auto& thread : threads) {
thread.join();
}
}
TEST_F(PartyTest, ThreadStressTestWithNonOwningWaker) {
auto party = MakeParty();
std::vector<std::thread> threads;
threads.reserve(8);
for (int i = 0; i < 8; i++) {
threads.emplace_back([party]() {
for (int i = 0; i < 100; i++) {
ExecCtx ctx; // needed for Sleep
PromiseNotification promise_start(false);
Notification promise_complete;
party->Spawn("TestSpawn",
Seq(promise_start.Wait(),
Sleep(Timestamp::Now() + Duration::Milliseconds(10)),
[]() -> Poll<int> { return 42; }),
[&promise_complete](int i) {
EXPECT_EQ(i, 42);
promise_complete.Notify();
});
promise_start.Notify();
promise_complete.WaitForNotification();
}
});
}
for (auto& thread : threads) {
thread.join();
}
}
TEST_F(PartyTest, ThreadStressTestWithOwningWakerNoSleep) {
auto party = MakeParty();
std::vector<std::thread> threads;
threads.reserve(8);
for (int i = 0; i < 8; i++) {
threads.emplace_back([party]() {
for (int i = 0; i < 10000; i++) {
PromiseNotification promise_start(true);
Notification promise_complete;
party->Spawn(
"TestSpawn",
Seq(promise_start.Wait(), []() -> Poll<int> { return 42; }),
[&promise_complete](int i) {
EXPECT_EQ(i, 42);
promise_complete.Notify();
});
promise_start.Notify();
promise_complete.WaitForNotification();
}
});
}
for (auto& thread : threads) {
thread.join();
}
}
TEST_F(PartyTest, ThreadStressTestWithNonOwningWakerNoSleep) {
auto party = MakeParty();
std::vector<std::thread> threads;
threads.reserve(8);
for (int i = 0; i < 8; i++) {
threads.emplace_back([party]() {
for (int i = 0; i < 10000; i++) {
PromiseNotification promise_start(false);
Notification promise_complete;
party->Spawn(
"TestSpawn",
Seq(promise_start.Wait(), []() -> Poll<int> { return 42; }),
[&promise_complete](int i) {
EXPECT_EQ(i, 42);
promise_complete.Notify();
});
promise_start.Notify();
promise_complete.WaitForNotification();
}
});
}
for (auto& thread : threads) {
thread.join();
}
}
TEST_F(PartyTest, ThreadStressTestWithInnerSpawn) {
auto party = MakeParty();
std::vector<std::thread> threads;
threads.reserve(8);
for (int i = 0; i < 8; i++) {
threads.emplace_back([party]() {
for (int i = 0; i < 100; i++) {
ExecCtx ctx; // needed for Sleep
PromiseNotification inner_start(true);
PromiseNotification inner_complete(false);
Notification promise_complete;
party->Spawn(
"TestSpawn",
Seq(
[party, &inner_start, &inner_complete]() -> Poll<int> {
party->Spawn("TestSpawnInner",
Seq(inner_start.Wait(), []() { return 0; }),
[&inner_complete](int i) {
EXPECT_EQ(i, 0);
inner_complete.Notify();
});
return 0;
},
Sleep(Timestamp::Now() + Duration::Milliseconds(10)),
[&inner_start]() {
inner_start.Notify();
return 0;
},
inner_complete.Wait(), []() -> Poll<int> { return 42; }),
[&promise_complete](int i) {
EXPECT_EQ(i, 42);
promise_complete.Notify();
});
promise_complete.WaitForNotification();
}
});
}
for (auto& thread : threads) {
thread.join();
}
}
TEST_F(PartyTest, NestedWakeup) {
auto party1 = MakeParty();
auto party2 = MakeParty();
auto party3 = MakeParty();
int whats_going_on = 0;
[party] Make it faster (#37132) Notes: * Adds a single participant `AddParticipant` variant for this common case (per #37056 which I'm abandoning) * Folds the `PartySyncUsingAtomics` class back into `Party`, removes the `PartySyncUsingMutex` class * Leverages this integration to find places where we're doing repeated CAS operations and folds them into single operations (for example an unlock/unref pair can be folded into a single CAS) * Also lowers some `CHECK` statements into `DCHECK` - which I think is appropriate given the performance sensitivity of this code * Adds code to deal with overflowing the number of participants added to a party -- for now we do a busy add by queuing to event engine and retrying -- this has the advantage of not adding cost to the normal path, but has the slightly worrying disadvantage of effectively being a busy poll. My expectation is that this will be ok in general (the condition clears very quickly), but if not we'll modify this to be a linked list of pending actions and take a hit on the fast path. * Simplifies `PartyIsOver` (per #37113 which I'm abandoning) * Keeps a per-object wakeup cache (`wakeup_mask_`) that is protected by the lock bit -- this allows waking up a participant during polling without resorting to an extra atomic operation - significantly speeding that wakeup path (17ns --> 6ns) Before: ``` ---------------------------------------------------------------------- Benchmark Time CPU Iterations ---------------------------------------------------------------------- BM_PartyCreate 142 ns 142 ns 44952269 BM_PartyCreate 73.8 ns 73.8 ns 44952269 BM_PartyCreate 72.6 ns 72.6 ns 44952269 BM_PartyCreate 72.5 ns 72.5 ns 44952269 BM_PartyCreate 72.4 ns 72.4 ns 44952269 BM_PartyCreate 72.5 ns 72.5 ns 44952269 BM_PartyCreate 72.5 ns 72.5 ns 44952269 BM_PartyCreate 72.6 ns 72.6 ns 44952269 BM_PartyCreate 72.2 ns 72.2 ns 44952269 BM_PartyCreate 72.5 ns 72.5 ns 44952269 BM_PartyCreate_mean 79.5 ns 79.5 ns 10 BM_PartyCreate_median 72.5 ns 72.5 ns 10 BM_PartyCreate_stddev 21.8 ns 21.8 ns 10 BM_PartyCreate_cv 27.46 % 27.46 % 10 BM_AddParticipant 35.3 ns 35.3 ns 197041251 BM_AddParticipant 35.3 ns 35.3 ns 197041251 BM_AddParticipant 35.1 ns 35.1 ns 197041251 BM_AddParticipant 35.4 ns 35.4 ns 197041251 BM_AddParticipant 35.3 ns 35.3 ns 197041251 BM_AddParticipant 35.2 ns 35.2 ns 197041251 BM_AddParticipant 35.9 ns 35.9 ns 197041251 BM_AddParticipant 36.0 ns 36.0 ns 197041251 BM_AddParticipant 35.8 ns 35.8 ns 197041251 BM_AddParticipant 36.0 ns 36.0 ns 197041251 BM_AddParticipant_mean 35.5 ns 35.5 ns 10 BM_AddParticipant_median 35.4 ns 35.4 ns 10 BM_AddParticipant_stddev 0.352 ns 0.352 ns 10 BM_AddParticipant_cv 0.99 % 0.99 % 10 BM_WakeupParticipant 17.1 ns 17.1 ns 406116840 BM_WakeupParticipant 16.9 ns 16.9 ns 406116840 BM_WakeupParticipant 16.8 ns 16.8 ns 406116840 BM_WakeupParticipant 16.8 ns 16.8 ns 406116840 BM_WakeupParticipant 16.9 ns 16.9 ns 406116840 BM_WakeupParticipant 16.9 ns 16.9 ns 406116840 BM_WakeupParticipant 17.0 ns 17.0 ns 406116840 BM_WakeupParticipant 17.0 ns 17.0 ns 406116840 BM_WakeupParticipant 16.9 ns 16.9 ns 406116840 BM_WakeupParticipant 17.0 ns 17.0 ns 406116840 BM_WakeupParticipant_mean 16.9 ns 16.9 ns 10 BM_WakeupParticipant_median 16.9 ns 16.9 ns 10 BM_WakeupParticipant_stddev 0.087 ns 0.087 ns 10 BM_WakeupParticipant_cv 0.51 % 0.51 % 10 ``` After: ``` ---------------------------------------------------------------------- Benchmark Time CPU Iterations ---------------------------------------------------------------------- BM_PartyCreate 115 ns 115 ns 29602192 BM_PartyCreate 56.5 ns 56.5 ns 29602192 BM_PartyCreate 55.3 ns 55.3 ns 29602192 BM_PartyCreate 55.9 ns 55.9 ns 29602192 BM_PartyCreate 55.1 ns 55.1 ns 29602192 BM_PartyCreate 55.2 ns 55.2 ns 29602192 BM_PartyCreate 55.2 ns 55.2 ns 29602192 BM_PartyCreate 56.2 ns 56.2 ns 29602192 BM_PartyCreate 54.7 ns 54.7 ns 29602192 BM_PartyCreate 55.8 ns 55.8 ns 29602192 BM_PartyCreate_mean 61.5 ns 61.5 ns 10 BM_PartyCreate_median 55.5 ns 55.5 ns 10 BM_PartyCreate_stddev 18.9 ns 18.9 ns 10 BM_PartyCreate_cv 30.68 % 30.68 % 10 BM_AddParticipant 26.9 ns 26.9 ns 155407231 BM_AddParticipant 26.5 ns 26.5 ns 155407231 BM_AddParticipant 24.8 ns 24.8 ns 155407231 BM_AddParticipant 24.9 ns 24.9 ns 155407231 BM_AddParticipant 24.8 ns 24.8 ns 155407231 BM_AddParticipant 25.3 ns 25.3 ns 155407231 BM_AddParticipant 25.8 ns 25.8 ns 155407231 BM_AddParticipant 25.3 ns 25.3 ns 155407231 BM_AddParticipant 30.8 ns 30.8 ns 155407231 BM_AddParticipant 27.7 ns 27.7 ns 155407231 BM_AddParticipant_mean 26.3 ns 26.3 ns 10 BM_AddParticipant_median 25.6 ns 25.6 ns 10 BM_AddParticipant_stddev 1.87 ns 1.87 ns 10 BM_AddParticipant_cv 7.11 % 7.10 % 10 BM_WakeupParticipant 6.75 ns 6.75 ns 623459241 BM_WakeupParticipant 6.77 ns 6.77 ns 623459241 BM_WakeupParticipant 6.74 ns 6.74 ns 623459241 BM_WakeupParticipant 6.73 ns 6.73 ns 623459241 BM_WakeupParticipant 6.74 ns 6.74 ns 623459241 BM_WakeupParticipant 6.70 ns 6.70 ns 623459241 BM_WakeupParticipant 6.70 ns 6.69 ns 623459241 BM_WakeupParticipant 6.79 ns 6.79 ns 623459241 BM_WakeupParticipant 6.76 ns 6.76 ns 623459241 BM_WakeupParticipant 6.78 ns 6.78 ns 623459241 BM_WakeupParticipant_mean 6.75 ns 6.75 ns 10 BM_WakeupParticipant_median 6.75 ns 6.75 ns 10 BM_WakeupParticipant_stddev 0.031 ns 0.031 ns 10 BM_WakeupParticipant_cv 0.46 % 0.46 % 10 ``` Closes #37132 COPYBARA_INTEGRATE_REVIEW=https://github.com/grpc/grpc/pull/37132 from ctiller:nineteen-ninety-nine 336c87bdd645133a174861adb902ca763e07f57e PiperOrigin-RevId: 656437265
5 months ago
Notification done1;
Notification started2;
Notification done2;
Notification started3;
Notification notify_done;
party1->Spawn(
"p1",
[&]() {
EXPECT_EQ(whats_going_on, 0);
whats_going_on = 1;
party2->Spawn(
"p2",
[&]() {
[party] Make it faster (#37132) Notes: * Adds a single participant `AddParticipant` variant for this common case (per #37056 which I'm abandoning) * Folds the `PartySyncUsingAtomics` class back into `Party`, removes the `PartySyncUsingMutex` class * Leverages this integration to find places where we're doing repeated CAS operations and folds them into single operations (for example an unlock/unref pair can be folded into a single CAS) * Also lowers some `CHECK` statements into `DCHECK` - which I think is appropriate given the performance sensitivity of this code * Adds code to deal with overflowing the number of participants added to a party -- for now we do a busy add by queuing to event engine and retrying -- this has the advantage of not adding cost to the normal path, but has the slightly worrying disadvantage of effectively being a busy poll. My expectation is that this will be ok in general (the condition clears very quickly), but if not we'll modify this to be a linked list of pending actions and take a hit on the fast path. * Simplifies `PartyIsOver` (per #37113 which I'm abandoning) * Keeps a per-object wakeup cache (`wakeup_mask_`) that is protected by the lock bit -- this allows waking up a participant during polling without resorting to an extra atomic operation - significantly speeding that wakeup path (17ns --> 6ns) Before: ``` ---------------------------------------------------------------------- Benchmark Time CPU Iterations ---------------------------------------------------------------------- BM_PartyCreate 142 ns 142 ns 44952269 BM_PartyCreate 73.8 ns 73.8 ns 44952269 BM_PartyCreate 72.6 ns 72.6 ns 44952269 BM_PartyCreate 72.5 ns 72.5 ns 44952269 BM_PartyCreate 72.4 ns 72.4 ns 44952269 BM_PartyCreate 72.5 ns 72.5 ns 44952269 BM_PartyCreate 72.5 ns 72.5 ns 44952269 BM_PartyCreate 72.6 ns 72.6 ns 44952269 BM_PartyCreate 72.2 ns 72.2 ns 44952269 BM_PartyCreate 72.5 ns 72.5 ns 44952269 BM_PartyCreate_mean 79.5 ns 79.5 ns 10 BM_PartyCreate_median 72.5 ns 72.5 ns 10 BM_PartyCreate_stddev 21.8 ns 21.8 ns 10 BM_PartyCreate_cv 27.46 % 27.46 % 10 BM_AddParticipant 35.3 ns 35.3 ns 197041251 BM_AddParticipant 35.3 ns 35.3 ns 197041251 BM_AddParticipant 35.1 ns 35.1 ns 197041251 BM_AddParticipant 35.4 ns 35.4 ns 197041251 BM_AddParticipant 35.3 ns 35.3 ns 197041251 BM_AddParticipant 35.2 ns 35.2 ns 197041251 BM_AddParticipant 35.9 ns 35.9 ns 197041251 BM_AddParticipant 36.0 ns 36.0 ns 197041251 BM_AddParticipant 35.8 ns 35.8 ns 197041251 BM_AddParticipant 36.0 ns 36.0 ns 197041251 BM_AddParticipant_mean 35.5 ns 35.5 ns 10 BM_AddParticipant_median 35.4 ns 35.4 ns 10 BM_AddParticipant_stddev 0.352 ns 0.352 ns 10 BM_AddParticipant_cv 0.99 % 0.99 % 10 BM_WakeupParticipant 17.1 ns 17.1 ns 406116840 BM_WakeupParticipant 16.9 ns 16.9 ns 406116840 BM_WakeupParticipant 16.8 ns 16.8 ns 406116840 BM_WakeupParticipant 16.8 ns 16.8 ns 406116840 BM_WakeupParticipant 16.9 ns 16.9 ns 406116840 BM_WakeupParticipant 16.9 ns 16.9 ns 406116840 BM_WakeupParticipant 17.0 ns 17.0 ns 406116840 BM_WakeupParticipant 17.0 ns 17.0 ns 406116840 BM_WakeupParticipant 16.9 ns 16.9 ns 406116840 BM_WakeupParticipant 17.0 ns 17.0 ns 406116840 BM_WakeupParticipant_mean 16.9 ns 16.9 ns 10 BM_WakeupParticipant_median 16.9 ns 16.9 ns 10 BM_WakeupParticipant_stddev 0.087 ns 0.087 ns 10 BM_WakeupParticipant_cv 0.51 % 0.51 % 10 ``` After: ``` ---------------------------------------------------------------------- Benchmark Time CPU Iterations ---------------------------------------------------------------------- BM_PartyCreate 115 ns 115 ns 29602192 BM_PartyCreate 56.5 ns 56.5 ns 29602192 BM_PartyCreate 55.3 ns 55.3 ns 29602192 BM_PartyCreate 55.9 ns 55.9 ns 29602192 BM_PartyCreate 55.1 ns 55.1 ns 29602192 BM_PartyCreate 55.2 ns 55.2 ns 29602192 BM_PartyCreate 55.2 ns 55.2 ns 29602192 BM_PartyCreate 56.2 ns 56.2 ns 29602192 BM_PartyCreate 54.7 ns 54.7 ns 29602192 BM_PartyCreate 55.8 ns 55.8 ns 29602192 BM_PartyCreate_mean 61.5 ns 61.5 ns 10 BM_PartyCreate_median 55.5 ns 55.5 ns 10 BM_PartyCreate_stddev 18.9 ns 18.9 ns 10 BM_PartyCreate_cv 30.68 % 30.68 % 10 BM_AddParticipant 26.9 ns 26.9 ns 155407231 BM_AddParticipant 26.5 ns 26.5 ns 155407231 BM_AddParticipant 24.8 ns 24.8 ns 155407231 BM_AddParticipant 24.9 ns 24.9 ns 155407231 BM_AddParticipant 24.8 ns 24.8 ns 155407231 BM_AddParticipant 25.3 ns 25.3 ns 155407231 BM_AddParticipant 25.8 ns 25.8 ns 155407231 BM_AddParticipant 25.3 ns 25.3 ns 155407231 BM_AddParticipant 30.8 ns 30.8 ns 155407231 BM_AddParticipant 27.7 ns 27.7 ns 155407231 BM_AddParticipant_mean 26.3 ns 26.3 ns 10 BM_AddParticipant_median 25.6 ns 25.6 ns 10 BM_AddParticipant_stddev 1.87 ns 1.87 ns 10 BM_AddParticipant_cv 7.11 % 7.10 % 10 BM_WakeupParticipant 6.75 ns 6.75 ns 623459241 BM_WakeupParticipant 6.77 ns 6.77 ns 623459241 BM_WakeupParticipant 6.74 ns 6.74 ns 623459241 BM_WakeupParticipant 6.73 ns 6.73 ns 623459241 BM_WakeupParticipant 6.74 ns 6.74 ns 623459241 BM_WakeupParticipant 6.70 ns 6.70 ns 623459241 BM_WakeupParticipant 6.70 ns 6.69 ns 623459241 BM_WakeupParticipant 6.79 ns 6.79 ns 623459241 BM_WakeupParticipant 6.76 ns 6.76 ns 623459241 BM_WakeupParticipant 6.78 ns 6.78 ns 623459241 BM_WakeupParticipant_mean 6.75 ns 6.75 ns 10 BM_WakeupParticipant_median 6.75 ns 6.75 ns 10 BM_WakeupParticipant_stddev 0.031 ns 0.031 ns 10 BM_WakeupParticipant_cv 0.46 % 0.46 % 10 ``` Closes #37132 COPYBARA_INTEGRATE_REVIEW=https://github.com/grpc/grpc/pull/37132 from ctiller:nineteen-ninety-nine 336c87bdd645133a174861adb902ca763e07f57e PiperOrigin-RevId: 656437265
5 months ago
done1.WaitForNotification();
started2.Notify();
started3.WaitForNotification();
EXPECT_EQ(whats_going_on, 3);
whats_going_on = 4;
return Empty{};
},
[&](Empty) {
EXPECT_EQ(whats_going_on, 4);
whats_going_on = 5;
done2.Notify();
});
party3->Spawn(
"p3",
[&]() {
started2.WaitForNotification();
started3.Notify();
done2.WaitForNotification();
EXPECT_EQ(whats_going_on, 5);
whats_going_on = 6;
return Empty{};
},
[&](Empty) {
EXPECT_EQ(whats_going_on, 6);
whats_going_on = 7;
notify_done.Notify();
});
EXPECT_EQ(whats_going_on, 1);
whats_going_on = 2;
return Empty{};
},
[&](Empty) {
EXPECT_EQ(whats_going_on, 2);
whats_going_on = 3;
[party] Make it faster (#37132) Notes: * Adds a single participant `AddParticipant` variant for this common case (per #37056 which I'm abandoning) * Folds the `PartySyncUsingAtomics` class back into `Party`, removes the `PartySyncUsingMutex` class * Leverages this integration to find places where we're doing repeated CAS operations and folds them into single operations (for example an unlock/unref pair can be folded into a single CAS) * Also lowers some `CHECK` statements into `DCHECK` - which I think is appropriate given the performance sensitivity of this code * Adds code to deal with overflowing the number of participants added to a party -- for now we do a busy add by queuing to event engine and retrying -- this has the advantage of not adding cost to the normal path, but has the slightly worrying disadvantage of effectively being a busy poll. My expectation is that this will be ok in general (the condition clears very quickly), but if not we'll modify this to be a linked list of pending actions and take a hit on the fast path. * Simplifies `PartyIsOver` (per #37113 which I'm abandoning) * Keeps a per-object wakeup cache (`wakeup_mask_`) that is protected by the lock bit -- this allows waking up a participant during polling without resorting to an extra atomic operation - significantly speeding that wakeup path (17ns --> 6ns) Before: ``` ---------------------------------------------------------------------- Benchmark Time CPU Iterations ---------------------------------------------------------------------- BM_PartyCreate 142 ns 142 ns 44952269 BM_PartyCreate 73.8 ns 73.8 ns 44952269 BM_PartyCreate 72.6 ns 72.6 ns 44952269 BM_PartyCreate 72.5 ns 72.5 ns 44952269 BM_PartyCreate 72.4 ns 72.4 ns 44952269 BM_PartyCreate 72.5 ns 72.5 ns 44952269 BM_PartyCreate 72.5 ns 72.5 ns 44952269 BM_PartyCreate 72.6 ns 72.6 ns 44952269 BM_PartyCreate 72.2 ns 72.2 ns 44952269 BM_PartyCreate 72.5 ns 72.5 ns 44952269 BM_PartyCreate_mean 79.5 ns 79.5 ns 10 BM_PartyCreate_median 72.5 ns 72.5 ns 10 BM_PartyCreate_stddev 21.8 ns 21.8 ns 10 BM_PartyCreate_cv 27.46 % 27.46 % 10 BM_AddParticipant 35.3 ns 35.3 ns 197041251 BM_AddParticipant 35.3 ns 35.3 ns 197041251 BM_AddParticipant 35.1 ns 35.1 ns 197041251 BM_AddParticipant 35.4 ns 35.4 ns 197041251 BM_AddParticipant 35.3 ns 35.3 ns 197041251 BM_AddParticipant 35.2 ns 35.2 ns 197041251 BM_AddParticipant 35.9 ns 35.9 ns 197041251 BM_AddParticipant 36.0 ns 36.0 ns 197041251 BM_AddParticipant 35.8 ns 35.8 ns 197041251 BM_AddParticipant 36.0 ns 36.0 ns 197041251 BM_AddParticipant_mean 35.5 ns 35.5 ns 10 BM_AddParticipant_median 35.4 ns 35.4 ns 10 BM_AddParticipant_stddev 0.352 ns 0.352 ns 10 BM_AddParticipant_cv 0.99 % 0.99 % 10 BM_WakeupParticipant 17.1 ns 17.1 ns 406116840 BM_WakeupParticipant 16.9 ns 16.9 ns 406116840 BM_WakeupParticipant 16.8 ns 16.8 ns 406116840 BM_WakeupParticipant 16.8 ns 16.8 ns 406116840 BM_WakeupParticipant 16.9 ns 16.9 ns 406116840 BM_WakeupParticipant 16.9 ns 16.9 ns 406116840 BM_WakeupParticipant 17.0 ns 17.0 ns 406116840 BM_WakeupParticipant 17.0 ns 17.0 ns 406116840 BM_WakeupParticipant 16.9 ns 16.9 ns 406116840 BM_WakeupParticipant 17.0 ns 17.0 ns 406116840 BM_WakeupParticipant_mean 16.9 ns 16.9 ns 10 BM_WakeupParticipant_median 16.9 ns 16.9 ns 10 BM_WakeupParticipant_stddev 0.087 ns 0.087 ns 10 BM_WakeupParticipant_cv 0.51 % 0.51 % 10 ``` After: ``` ---------------------------------------------------------------------- Benchmark Time CPU Iterations ---------------------------------------------------------------------- BM_PartyCreate 115 ns 115 ns 29602192 BM_PartyCreate 56.5 ns 56.5 ns 29602192 BM_PartyCreate 55.3 ns 55.3 ns 29602192 BM_PartyCreate 55.9 ns 55.9 ns 29602192 BM_PartyCreate 55.1 ns 55.1 ns 29602192 BM_PartyCreate 55.2 ns 55.2 ns 29602192 BM_PartyCreate 55.2 ns 55.2 ns 29602192 BM_PartyCreate 56.2 ns 56.2 ns 29602192 BM_PartyCreate 54.7 ns 54.7 ns 29602192 BM_PartyCreate 55.8 ns 55.8 ns 29602192 BM_PartyCreate_mean 61.5 ns 61.5 ns 10 BM_PartyCreate_median 55.5 ns 55.5 ns 10 BM_PartyCreate_stddev 18.9 ns 18.9 ns 10 BM_PartyCreate_cv 30.68 % 30.68 % 10 BM_AddParticipant 26.9 ns 26.9 ns 155407231 BM_AddParticipant 26.5 ns 26.5 ns 155407231 BM_AddParticipant 24.8 ns 24.8 ns 155407231 BM_AddParticipant 24.9 ns 24.9 ns 155407231 BM_AddParticipant 24.8 ns 24.8 ns 155407231 BM_AddParticipant 25.3 ns 25.3 ns 155407231 BM_AddParticipant 25.8 ns 25.8 ns 155407231 BM_AddParticipant 25.3 ns 25.3 ns 155407231 BM_AddParticipant 30.8 ns 30.8 ns 155407231 BM_AddParticipant 27.7 ns 27.7 ns 155407231 BM_AddParticipant_mean 26.3 ns 26.3 ns 10 BM_AddParticipant_median 25.6 ns 25.6 ns 10 BM_AddParticipant_stddev 1.87 ns 1.87 ns 10 BM_AddParticipant_cv 7.11 % 7.10 % 10 BM_WakeupParticipant 6.75 ns 6.75 ns 623459241 BM_WakeupParticipant 6.77 ns 6.77 ns 623459241 BM_WakeupParticipant 6.74 ns 6.74 ns 623459241 BM_WakeupParticipant 6.73 ns 6.73 ns 623459241 BM_WakeupParticipant 6.74 ns 6.74 ns 623459241 BM_WakeupParticipant 6.70 ns 6.70 ns 623459241 BM_WakeupParticipant 6.70 ns 6.69 ns 623459241 BM_WakeupParticipant 6.79 ns 6.79 ns 623459241 BM_WakeupParticipant 6.76 ns 6.76 ns 623459241 BM_WakeupParticipant 6.78 ns 6.78 ns 623459241 BM_WakeupParticipant_mean 6.75 ns 6.75 ns 10 BM_WakeupParticipant_median 6.75 ns 6.75 ns 10 BM_WakeupParticipant_stddev 0.031 ns 0.031 ns 10 BM_WakeupParticipant_cv 0.46 % 0.46 % 10 ``` Closes #37132 COPYBARA_INTEGRATE_REVIEW=https://github.com/grpc/grpc/pull/37132 from ctiller:nineteen-ninety-nine 336c87bdd645133a174861adb902ca763e07f57e PiperOrigin-RevId: 656437265
5 months ago
done1.Notify();
});
notify_done.WaitForNotification();
}
} // namespace grpc_core
int main(int argc, char** argv) {
::testing::InitGoogleTest(&argc, argv);
grpc_init();
int r = RUN_ALL_TESTS();
grpc_shutdown();
return r;
}