mirror of https://github.com/grpc/grpc.git
[backoff] Add random early detection classifier (#32354)
<!-- If you know who should review your pull request, please assign it to that person, otherwise the pull request would get assigned randomly. If your pull request is for a specific language, please add the appropriate lang label. --> --------- Co-authored-by: ctiller <ctiller@users.noreply.github.com>pull/32427/head
parent
7fab06b923
commit
d49e151306
9 changed files with 250 additions and 3 deletions
@ -0,0 +1,31 @@ |
||||
// 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 <grpc/support/port_platform.h> |
||||
|
||||
#include "src/core/lib/backoff/random_early_detection.h" |
||||
|
||||
namespace grpc_core { |
||||
|
||||
bool RandomEarlyDetection::Reject(uint64_t size) { |
||||
if (size <= soft_limit_) return false; |
||||
if (size < hard_limit_) { |
||||
return absl::Bernoulli(bitgen_, |
||||
static_cast<double>(size - soft_limit_) / |
||||
static_cast<double>(hard_limit_ - soft_limit_)); |
||||
} |
||||
return true; |
||||
} |
||||
|
||||
} // namespace grpc_core
|
@ -0,0 +1,56 @@ |
||||
// 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.
|
||||
|
||||
#ifndef GRPC_SRC_CORE_LIB_BACKOFF_RANDOM_EARLY_DETECTION_H |
||||
#define GRPC_SRC_CORE_LIB_BACKOFF_RANDOM_EARLY_DETECTION_H |
||||
|
||||
#include <grpc/support/port_platform.h> |
||||
|
||||
#include <cstdint> |
||||
|
||||
#include "absl/random/random.h" |
||||
|
||||
namespace grpc_core { |
||||
|
||||
// Implements the random early detection algorithm - allows items to be rejected
|
||||
// or accepted based upon their size.
|
||||
class RandomEarlyDetection { |
||||
public: |
||||
RandomEarlyDetection(uint64_t soft_limit, uint64_t hard_limit) |
||||
: soft_limit_(soft_limit), hard_limit_(hard_limit) {} |
||||
|
||||
// Returns true if the size is greater than or equal to the hard limit - ie if
|
||||
// this item must be rejected.
|
||||
bool MustReject(uint64_t size) { return size >= hard_limit_; } |
||||
|
||||
// Returns true if the item should be rejected.
|
||||
bool Reject(uint64_t size); |
||||
|
||||
uint64_t soft_limit() const { return soft_limit_; } |
||||
uint64_t hard_limit() const { return hard_limit_; } |
||||
|
||||
private: |
||||
// The soft limit is the size at which we start rejecting items with a
|
||||
// probability that increases linearly to 1 as the size approaches the hard
|
||||
// limit.
|
||||
uint64_t soft_limit_; |
||||
// The hard limit is the size at which we reject all items.
|
||||
uint64_t hard_limit_; |
||||
// The bit generator used to generate random numbers.
|
||||
absl::InsecureBitGen bitgen_; |
||||
}; |
||||
|
||||
} // namespace grpc_core
|
||||
|
||||
#endif // GRPC_SRC_CORE_LIB_BACKOFF_RANDOM_EARLY_DETECTION_H
|
@ -0,0 +1,63 @@ |
||||
// 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/backoff/random_early_detection.h" |
||||
|
||||
#include "gtest/gtest.h" |
||||
|
||||
namespace grpc_core { |
||||
namespace { |
||||
|
||||
TEST(RandomEarlyDetectionTest, NoOp) { |
||||
RandomEarlyDetection red(100, 200); |
||||
EXPECT_EQ(red.soft_limit(), 100); |
||||
EXPECT_EQ(red.hard_limit(), 200); |
||||
} |
||||
|
||||
TEST(RandomEarlyDetectionTest, Distribution) { |
||||
RandomEarlyDetection red(100, 200); |
||||
int64_t counts[300] = {}; |
||||
for (int round = 0; round < 10000; round++) { |
||||
for (int64_t i = 0; i < 300; i++) { |
||||
if (red.Reject(i)) counts[i]++; |
||||
} |
||||
} |
||||
for (int64_t i = 0; i < 100; i++) { |
||||
// [0, 100) should never be rejected
|
||||
EXPECT_EQ(counts[i], 0) << i; |
||||
// [100, 200) should be rejected with probability ramping from 0 to 1
|
||||
EXPECT_GT(counts[i + 100], (i - 5) * 100) << i; |
||||
EXPECT_LT(counts[i + 100], (i + 5) * 100) << i; |
||||
// [200, 300) should always be rejected
|
||||
EXPECT_EQ(counts[i + 200], 10000) << i; |
||||
} |
||||
} |
||||
|
||||
TEST(RandomEarlyDetection, MustRejectWorks) { |
||||
RandomEarlyDetection red(100, 200); |
||||
for (int64_t i = 0; i < 200; i++) { |
||||
EXPECT_FALSE(red.MustReject(i)); |
||||
} |
||||
for (int64_t i = 200; i < 300; i++) { |
||||
EXPECT_TRUE(red.MustReject(i)); |
||||
} |
||||
} |
||||
|
||||
} // namespace
|
||||
} // namespace grpc_core
|
||||
|
||||
int main(int argc, char** argv) { |
||||
::testing::InitGoogleTest(&argc, argv); |
||||
return RUN_ALL_TESTS(); |
||||
} |
Loading…
Reference in new issue