|
1 | 1 | #include <gtest/gtest.h> |
| 2 | +#include <vector> |
2 | 3 |
|
| 4 | +#include "common/type.h" |
3 | 5 | #include "controlplane/segment_allocator.h" |
4 | 6 |
|
5 | 7 | static constexpr uint32_t error_result = 0; |
@@ -225,6 +227,215 @@ TEST(SegmentAllocator, OnlyErrors) |
225 | 227 | EXPECT_EQ(allocator.GetErrors(), Errors(9, 0)); |
226 | 228 | } |
227 | 229 |
|
| 230 | +bool TestBlockSegmentAllocator(uint16_t segment_size, uint16_t groups, uint16_t count_group) |
| 231 | +{ |
| 232 | + static constexpr uint32_t counter_index_begin = (((uint32_t)common::globalBase::static_counter_type::size + 63) / 64) * 64; |
| 233 | + static constexpr uint32_t max_buffer_size = 64; |
| 234 | + using SegmentAllocatorType = SegmentAllocator<counter_index_begin, YANET_CONFIG_COUNTERS_SIZE, 64 * 64, max_buffer_size, 0>; |
| 235 | + SegmentAllocatorType::OneBlock block; |
| 236 | + |
| 237 | + block.Initialize(0, 0); |
| 238 | + block.SetSize(segment_size); |
| 239 | + int free_segments = block.free_segments; |
| 240 | + |
| 241 | + for (uint32_t retries = 0; retries < groups; retries++) |
| 242 | + { |
| 243 | + std::vector<uint16_t> data; |
| 244 | + // Try allocate "count_group" segments |
| 245 | + for (uint16_t index = 0; index < count_group && free_segments > 0; index++) |
| 246 | + { |
| 247 | + uint16_t value = block.Allocate(); |
| 248 | + if (value == SegmentAllocatorType::error_in_block_) |
| 249 | + { |
| 250 | + std::cerr << "Error allocate segment in block\n"; |
| 251 | + return false; |
| 252 | + } |
| 253 | + free_segments--; |
| 254 | + data.push_back(value); |
| 255 | + |
| 256 | + if (!block.CheckInvariants()) |
| 257 | + { |
| 258 | + return false; |
| 259 | + } |
| 260 | + } |
| 261 | + |
| 262 | + for (uint16_t index = 0; index < data.size(); index += 2) |
| 263 | + { |
| 264 | + if (block.Free(data[index]) == SegmentAllocatorType::error_in_block_) |
| 265 | + { |
| 266 | + std::cout << "Error free segment in block\n"; |
| 267 | + return false; |
| 268 | + } |
| 269 | + free_segments++; |
| 270 | + |
| 271 | + if (!block.CheckInvariants()) |
| 272 | + { |
| 273 | + return false; |
| 274 | + } |
| 275 | + } |
| 276 | + } |
| 277 | + |
| 278 | + return true; |
| 279 | +} |
| 280 | + |
| 281 | +TEST(SegmentAllocator, Block) |
| 282 | +{ |
| 283 | + EXPECT_TRUE(TestBlockSegmentAllocator(2, 10, 1000)); |
| 284 | + EXPECT_TRUE(TestBlockSegmentAllocator(4, 10, 500)); |
| 285 | + EXPECT_TRUE(TestBlockSegmentAllocator(6, 10, 500)); |
| 286 | + EXPECT_TRUE(TestBlockSegmentAllocator(37, 10, 100)); |
| 287 | +} |
| 288 | + |
| 289 | +struct TestOneSizeInfo |
| 290 | +{ |
| 291 | + uint32_t size; |
| 292 | + uint32_t accumulated_weight; |
| 293 | + uint32_t elements_in_block; |
| 294 | + uint32_t used_blocks = 0; |
| 295 | + std::vector<uint32_t> segments; |
| 296 | + std::set<uint32_t> segments_map; |
| 297 | +}; |
| 298 | + |
| 299 | +bool TestSegmentAllocatorDifferentSizes(std::vector<std::pair<uint32_t, uint32_t>> test_data) |
| 300 | +{ |
| 301 | + static constexpr uint32_t counter_index_begin = (((uint32_t)common::globalBase::static_counter_type::size + 63) / 64) * 64; |
| 302 | + static constexpr uint32_t max_buffer_size = 64; |
| 303 | + static constexpr uint32_t block_size = 64 * 64; |
| 304 | + static constexpr uint32_t counters_size = YANET_CONFIG_COUNTERS_SIZE / 8; |
| 305 | + static constexpr uint32_t total_full_blocks = (counters_size - counter_index_begin) / block_size; |
| 306 | + SegmentAllocator<counter_index_begin, counters_size, block_size, max_buffer_size, 0> allocator; |
| 307 | + |
| 308 | + uint32_t sizes = test_data.size(); |
| 309 | + uint32_t total_weight = 0; |
| 310 | + std::vector<TestOneSizeInfo> tests_info(sizes); |
| 311 | + for (uint32_t index = 0; index < sizes; index++) |
| 312 | + { |
| 313 | + total_weight += test_data[index].second; |
| 314 | + TestOneSizeInfo& test_info = tests_info[index]; |
| 315 | + test_info.size = test_data[index].first; |
| 316 | + test_info.accumulated_weight = total_weight; |
| 317 | + test_info.elements_in_block = block_size / test_info.size; |
| 318 | + } |
| 319 | + |
| 320 | + std::srand(17); |
| 321 | + uint32_t used_blocks = 0; |
| 322 | + while (used_blocks + sizes <= total_full_blocks) |
| 323 | + { |
| 324 | + // Select size |
| 325 | + uint32_t weight = std::rand() % total_weight; |
| 326 | + uint32_t cur_index = 0; |
| 327 | + for (uint32_t index = 1; index < sizes; index++) |
| 328 | + { |
| 329 | + if (weight >= tests_info[index - 1].accumulated_weight) |
| 330 | + { |
| 331 | + cur_index = index; |
| 332 | + } |
| 333 | + } |
| 334 | + |
| 335 | + // Select action |
| 336 | + TestOneSizeInfo& test_info = tests_info[cur_index]; |
| 337 | + static constexpr int32_t probability_free = 30; |
| 338 | + bool action_free = ((std::rand() % 100) < probability_free); |
| 339 | + if (action_free) |
| 340 | + { |
| 341 | + // Free |
| 342 | + uint32_t current_size = test_info.segments.size(); |
| 343 | + if (current_size != 0) |
| 344 | + { |
| 345 | + uint32_t index = std::rand() % current_size; |
| 346 | + uint32_t value = test_info.segments[index]; |
| 347 | + test_info.segments_map.erase(value); |
| 348 | + test_info.segments[index] = test_info.segments.back(); |
| 349 | + test_info.segments.pop_back(); |
| 350 | + |
| 351 | + if (allocator.Free(value, test_info.size) == 0) |
| 352 | + { |
| 353 | + std::cout << "Error free segment in allocator\n"; |
| 354 | + return false; |
| 355 | + } |
| 356 | + } |
| 357 | + } |
| 358 | + else |
| 359 | + { |
| 360 | + // Allocate |
| 361 | + uint32_t value = allocator.Allocate(test_info.size); |
| 362 | + if (value == 0) |
| 363 | + { |
| 364 | + std::cout << "can't allocate\n"; |
| 365 | + return false; |
| 366 | + } |
| 367 | + else if (test_info.segments_map.find(value) != test_info.segments_map.end()) |
| 368 | + { |
| 369 | + std::cout << "Allocated segment exists\n"; |
| 370 | + return false; |
| 371 | + } |
| 372 | + test_info.segments_map.insert(value); |
| 373 | + test_info.segments.push_back(value); |
| 374 | + if (test_info.segments.size() % test_info.elements_in_block == 1) |
| 375 | + { |
| 376 | + used_blocks = 0; |
| 377 | + for (const TestOneSizeInfo& info : tests_info) |
| 378 | + { |
| 379 | + used_blocks += (info.segments.size() + info.elements_in_block - 1) / info.elements_in_block; |
| 380 | + } |
| 381 | + } |
| 382 | + } |
| 383 | + } |
| 384 | + |
| 385 | + EXPECT_EQ(allocator.GetErrors(), Errors(0, 0)); |
| 386 | + |
| 387 | + uint32_t used_counters = 0; |
| 388 | + for (const TestOneSizeInfo& info : tests_info) |
| 389 | + { |
| 390 | + used_counters += info.size * info.segments.size(); |
| 391 | + } |
| 392 | + uint32_t total_counters = counters_size - counter_index_begin; |
| 393 | + EXPECT_GE(used_counters, 0.95 * total_counters); |
| 394 | + |
| 395 | + return true; |
| 396 | +} |
| 397 | + |
| 398 | +TEST(SegmentAllocator, DifferentSizes) |
| 399 | +{ |
| 400 | + EXPECT_TRUE(TestSegmentAllocatorDifferentSizes({{2, 10}, {4, 5}, {6, 1}})); |
| 401 | + EXPECT_TRUE(TestSegmentAllocatorDifferentSizes({{8, 1}, {17, 1}, {37, 1}})); |
| 402 | +} |
| 403 | + |
| 404 | +TEST(SegmentAllocator, AddAndHalfDelete) |
| 405 | +{ |
| 406 | + static constexpr uint32_t counter_index_begin = (((uint32_t)common::globalBase::static_counter_type::size + 63) / 64) * 64; |
| 407 | + static constexpr uint32_t max_buffer_size = 64; |
| 408 | + SegmentAllocator<counter_index_begin, YANET_CONFIG_COUNTERS_SIZE, 64 * 64, max_buffer_size, 0> allocator; |
| 409 | + |
| 410 | + uint16_t size = 2; |
| 411 | + uint32_t count = 100000; |
| 412 | + bool errors = false; |
| 413 | + for (uint32_t retries = 0; retries < 10 && !errors; retries++) |
| 414 | + { |
| 415 | + std::vector<uint32_t> data; |
| 416 | + |
| 417 | + for (uint32_t index = 0; index < count && !errors; index++) |
| 418 | + { |
| 419 | + uint32_t value = allocator.Allocate(size); |
| 420 | + EXPECT_NE(value, 0); |
| 421 | + if (value == 0) |
| 422 | + { |
| 423 | + errors = true; |
| 424 | + break; |
| 425 | + } |
| 426 | + data.push_back(value); |
| 427 | + } |
| 428 | + |
| 429 | + for (uint32_t index = 0; index < count && !errors; index += 2) |
| 430 | + { |
| 431 | + EXPECT_TRUE(allocator.Free(data[index], size)); |
| 432 | + } |
| 433 | + |
| 434 | + EXPECT_EQ(allocator.GetErrors(), Errors(0, 0)); |
| 435 | + } |
| 436 | + EXPECT_EQ(allocator.GetErrors(), Errors(0, 0)); |
| 437 | +} |
| 438 | + |
228 | 439 | int main(int argc, char** argv) |
229 | 440 | { |
230 | 441 | ::testing::InitGoogleTest(&argc, argv); |
|
0 commit comments