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