//------------------------------------------------------------------------------ //! @file BufferManagerTests.cc //! @author Elvin Sindrilaru - CERN //------------------------------------------------------------------------------ /************************************************************************ * EOS - the CERN Disk Storage System * * Copyright (C) 2020 CERN/Switzerland * * * * This program is free software: you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation, either version 3 of the License, or * * (at your option) any later version. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU General Public License * * along with this program. If not, see .* ************************************************************************/ #include "gtest/gtest.h" #define IN_TEST_HARNESS #include "common/BufferManager.hh" #undef IN_TEST_HARNESS #include "common/StringConversion.hh" #include #include #include TEST(BufferManager, PowerCeil) { ASSERT_EQ(1024, eos::common::GetPowerCeil(1)); ASSERT_EQ(1024, eos::common::GetPowerCeil(1000)); ASSERT_EQ(1024, eos::common::GetPowerCeil(1024)); ASSERT_EQ(2048, eos::common::GetPowerCeil(1025)); ASSERT_EQ(4096, eos::common::GetPowerCeil(2049)); ASSERT_EQ(8192, eos::common::GetPowerCeil(5000)); ASSERT_EQ(16384, eos::common::GetPowerCeil(9001)); ASSERT_EQ(2048, eos::common::GetPowerCeil(1, 2048)); ASSERT_EQ(4096, eos::common::GetPowerCeil(2049, 2048)); ASSERT_EQ(16384, eos::common::GetPowerCeil(1, 16384)); ASSERT_EQ(32768, eos::common::GetPowerCeil(16385, 16384)); } TEST(BufferManager, MatchingSizes) { using namespace eos::common; eos::common::BufferManager buff_mgr(20 * MB); uint64_t buff_sz = 512 * KB; auto buffer = buff_mgr.GetBuffer(buff_sz); ASSERT_NE(buffer, nullptr); ASSERT_EQ(buffer->mCapacity, 1 * MB); buff_sz = 1 * MB; buffer = buff_mgr.GetBuffer(buff_sz); ASSERT_NE(buffer, nullptr); ASSERT_EQ(buffer->mCapacity, 1 * MB); buff_sz = 1; buffer = buff_mgr.GetBuffer(buff_sz); ASSERT_NE(buffer, nullptr); ASSERT_EQ(buffer->mCapacity, 1 * MB); buff_sz = 1 * MB + 22 * KB; buffer = buff_mgr.GetBuffer(buff_sz); ASSERT_NE(buffer, nullptr); ASSERT_EQ(buffer->mCapacity, 2 * MB); buff_sz = 1 * MB + 44 * KB; buffer = buff_mgr.GetBuffer(buff_sz); ASSERT_NE(buffer, nullptr); ASSERT_EQ(buffer->mCapacity, 2 * MB); buff_sz = 3 * MB + 11 * KB; buffer = buff_mgr.GetBuffer(buff_sz); ASSERT_NE(buffer, nullptr); ASSERT_EQ(buffer->mCapacity, 4 * MB); buff_sz = 512 * MB + 33 * KB; buffer = buff_mgr.GetBuffer(buff_sz); ASSERT_EQ(buffer, nullptr); uint64_t total_size {0ull}; auto slot_sizes = buff_mgr.GetSortedSlotSizes(total_size); ASSERT_EQ(11 * MB, total_size); // By default there are 6 slots populated like this // index: 0 slot: 3 size: 0 // index: 1 slot: 4 size: 0 // index: 2 slot: 5 size: 0 // index: 3 slot: 6 size: 0 // index: 4 slot: 0 size: 3145728 // index: 5 slot: 1 size: 4194304 // index: 6 slot: 2 size: 4194304 ASSERT_EQ(3, slot_sizes[0].first); ASSERT_EQ(0, slot_sizes[0].second); ASSERT_EQ(4, slot_sizes[1].first); ASSERT_EQ(0, slot_sizes[1].second); ASSERT_EQ(5, slot_sizes[2].first); ASSERT_EQ(0, slot_sizes[2].second); ASSERT_EQ(6, slot_sizes[3].first); ASSERT_EQ(0, slot_sizes[3].second); ASSERT_EQ(0, slot_sizes[4].first); ASSERT_EQ(3145728, slot_sizes[4].second); ASSERT_EQ(1, slot_sizes[5].first); ASSERT_EQ(4194304, slot_sizes[5].second); ASSERT_EQ(2, slot_sizes[6].first); ASSERT_EQ(4194304, slot_sizes[6].second); } TEST(BufferManager, RecycleSingleBuffer) { using namespace eos::common; eos::common::BufferManager buff_mgr(20 * MB); for (int i = 0; i < 100; ++i) { auto buffer = buff_mgr.GetBuffer(1 * MB); buff_mgr.Recycle(buffer); } uint64_t total_size {0ull}; auto sorted_slots = buff_mgr.GetSortedSlotSizes(total_size); ASSERT_EQ(total_size, 1 * MB); } TEST(BufferManager, AdjustCachedSizes) { using namespace eos::common; eos::common::BufferManager buff_mgr(20 * MB); std::list> lst_buffs; // Recycle a 1MB blocks in a loop for (int i = 0; i < 20; ++i) { lst_buffs.push_back(buff_mgr.GetBuffer(1 * MB)); // do some work with the buffer buff_mgr.Recycle(lst_buffs.back()); lst_buffs.pop_back(); } uint64_t total_size {0ull}; auto sorted_slots = buff_mgr.GetSortedSlotSizes(total_size); ASSERT_EQ(total_size, 1 * MB); // Fill cache with 1MB blocks for (int i = 0; i < 20; ++i) { lst_buffs.push_back(buff_mgr.GetBuffer(1 * MB)); } for (int i = 0; i < 20; ++i) { buff_mgr.Recycle(lst_buffs.back()); lst_buffs.pop_back(); } sorted_slots = buff_mgr.GetSortedSlotSizes(total_size); ASSERT_EQ(total_size, 20 * MB); auto buffer = buff_mgr.GetBuffer(3 * MB); ASSERT_NE(buffer, nullptr); ASSERT_EQ(buffer->mCapacity, 4 * MB); buff_mgr.Recycle(buffer); sorted_slots = buff_mgr.GetSortedSlotSizes(total_size); ASSERT_EQ(total_size, 16 * MB); } TEST(BufferManager, MultipleThreads) { using namespace eos::common; auto work = [](eos::common::BufferManager & buff_mgr, int num_blocks, float mean, float stddev) { std::random_device rd; std::mt19937 gen(rd()); std::normal_distribution<> dis(mean, stddev); for (int i = 0; i < num_blocks; ++i) { uint64_t max_buff_sz = (1 << buff_mgr.GetNumSlots()) * MB; uint64_t value = std::round(std::abs(dis(gen))); // Make sure the generated value is within limits if (value <= 0) { value = 1; } else if (value > max_buff_sz) { value = max_buff_sz; } auto buffer = buff_mgr.GetBuffer(value); ASSERT_NE(buffer, nullptr); std::this_thread::sleep_for(std::chrono::milliseconds(50)); buff_mgr.Recycle(buffer); } }; int num_threads = 32; int num_blocks = 24; std::list lst_threads; std::list> uniform_dist_params; uniform_dist_params.emplace_back(500 * KB, 200 * KB); uniform_dist_params.emplace_back(1500 * KB, 200 * KB); uniform_dist_params.emplace_back(3500 * KB, 500 * KB); for (auto& dis_params : uniform_dist_params) { eos::common::BufferManager buff_mgr(100 * MB); for (int i = 0; i < num_threads; ++i) { lst_threads.emplace_back(work, std::ref(buff_mgr), num_blocks, dis_params.first, dis_params.second); } for (auto& job : lst_threads) { job.join(); } lst_threads.clear(); uint64_t total_size {0ull}; auto sorted_slots = buff_mgr.GetSortedSlotSizes(total_size); ASSERT_LE(total_size, buff_mgr.GetMaxSize()); // Get the most used slot according to the distribution mean uint32_t slot {UINT32_MAX}; for (auto i = 0ull; i <= buff_mgr.GetNumSlots(); ++i) { if (dis_params.first <= (1 << (i + 20))) { slot = i; break; } } // The slot with the largest allocated size should be "slot" // for (auto& elem: sorted_slots) { // std::cerr << "Slot: " << elem.first << " size: " << elem.second << std::endl; // } ASSERT_EQ(sorted_slots.rbegin()->first, slot); } }