| // Licensed to the Apache Software Foundation (ASF) under one |
| // or more contributor license agreements. See the NOTICE file |
| // distributed with this work for additional information |
| // regarding copyright ownership. The ASF licenses this file |
| // to you 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 "runtime/mem_pool.h" |
| |
| #include <gtest/gtest.h> |
| |
| #include <string> |
| |
| #include "runtime/mem_tracker.h" |
| #include "util/logging.h" |
| |
| namespace doris { |
| |
| TEST(MemPoolTest, Basic) { |
| MemTracker tracker(-1); |
| MemPool p(&tracker); |
| MemPool p2(&tracker); |
| MemPool p3(&tracker); |
| |
| // allocate a total of 24K in 32-byte pieces (for which we only request 25 bytes) |
| for (int i = 0; i < 768; ++i) { |
| // pads to 32 bytes |
| p.allocate(25); |
| } |
| // we handed back 24K, (4, 8 16) first allocate don't need padding |
| EXPECT_EQ(24 * 1024 - 3 * 7, p.total_allocated_bytes()); // 32 * 768 == 24 * 1024 |
| // .. and allocated 28K of chunks (4, 8, 16) |
| EXPECT_EQ(28 * 1024, p.total_reserved_bytes()); |
| |
| // we're passing on the first two chunks, containing 12K of data; we're left with one |
| // chunk of 16K containing 12K of data |
| p2.acquire_data(&p, true); |
| EXPECT_EQ(12 * 1024 - 7, p.total_allocated_bytes()); |
| EXPECT_EQ(16 * 1024, p.total_reserved_bytes()); |
| |
| // we allocate 8K, for which there isn't enough room in the current chunk, |
| // so another one is allocated (32K) |
| p.allocate(8 * 1024); |
| EXPECT_EQ((16 + 32) * 1024, p.total_reserved_bytes()); |
| |
| // we allocate 65K, which doesn't fit into the current chunk or the default |
| // size of the next allocated chunk (64K) |
| p.allocate(65 * 1024); |
| EXPECT_EQ((12 + 8 + 65) * 1024 - 7, p.total_allocated_bytes()); |
| EXPECT_EQ((16 + 32 + 128) * 1024, p.total_reserved_bytes()); |
| |
| // Clear() resets allocated data, but doesn't remove any chunks |
| p.clear(); |
| EXPECT_EQ(0, p.total_allocated_bytes()); |
| EXPECT_EQ((16 + 32 + 128) * 1024, p.total_reserved_bytes()); |
| |
| // next allocation reuses existing chunks |
| p.allocate(1024); |
| EXPECT_EQ(1024, p.total_allocated_bytes()); |
| EXPECT_EQ((16 + 32 + 128) * 1024, p.total_reserved_bytes()); |
| |
| // ... unless it doesn't fit into any available chunk |
| p.allocate(120 * 1024); |
| EXPECT_EQ((1 + 120) * 1024, p.total_allocated_bytes()); |
| EXPECT_EQ((16 + 32 + 128) * 1024, p.total_reserved_bytes()); |
| |
| // ... Try another chunk that fits into an existing chunk |
| p.allocate(33 * 1024); |
| EXPECT_EQ((1 + 120 + 33) * 1024, p.total_allocated_bytes()); |
| EXPECT_EQ((16 + 32 + 128 + 256) * 1024, p.total_reserved_bytes()); |
| |
| // we're releasing 3 chunks, which get added to p2 |
| p2.acquire_data(&p, false); |
| EXPECT_EQ(0, p.total_allocated_bytes()); |
| EXPECT_EQ(0, p.total_reserved_bytes()); |
| |
| p3.acquire_data(&p2, true); // we're keeping the 65k chunk |
| EXPECT_EQ(33 * 1024, p2.total_allocated_bytes()); |
| EXPECT_EQ(256 * 1024, p2.total_reserved_bytes()); |
| |
| { |
| MemPool p4(&tracker); |
| p4.exchange_data(&p2); |
| EXPECT_EQ(33 * 1024, p4.total_allocated_bytes()); |
| EXPECT_EQ(256 * 1024, p4.total_reserved_bytes()); |
| } |
| } |
| |
| // Test that we can keep an allocated chunk and a free chunk. |
| // This case verifies that when chunks are acquired by another memory pool the |
| // remaining chunks are consistent if there were more than one used chunk and some |
| // free chunks. |
| TEST(MemPoolTest, Keep) { |
| MemTracker tracker(-1); |
| MemPool p(&tracker); |
| p.allocate(4 * 1024); |
| p.allocate(8 * 1024); |
| p.allocate(16 * 1024); |
| EXPECT_EQ(p.total_allocated_bytes(), (4 + 8 + 16) * 1024); |
| EXPECT_EQ(p.total_reserved_bytes(), (4 + 8 + 16) * 1024); |
| p.clear(); |
| EXPECT_EQ(p.total_allocated_bytes(), 0); |
| EXPECT_EQ(p.total_reserved_bytes(), (4 + 8 + 16) * 1024); |
| p.allocate(1 * 1024); |
| p.allocate(4 * 1024); |
| EXPECT_EQ(p.total_allocated_bytes(), (1 + 4) * 1024); |
| EXPECT_EQ(p.total_reserved_bytes(), (4 + 8 + 16) * 1024); |
| MemPool p2(&tracker); |
| p2.acquire_data(&p, true); |
| |
| { |
| p2.exchange_data(&p); |
| EXPECT_EQ(4 * 1024, p2.total_allocated_bytes()); |
| EXPECT_EQ((8 + 16) * 1024, p2.total_reserved_bytes()); |
| EXPECT_EQ(1 * 1024, p.total_allocated_bytes()); |
| EXPECT_EQ(4 * 1024, p.total_reserved_bytes()); |
| } |
| } |
| |
| // Maximum allocation size which exceeds 32-bit. |
| #define LARGE_ALLOC_SIZE (1LL << 32) |
| |
| TEST(MemPoolTest, MaxAllocation) { |
| int64_t int_max_rounded = BitUtil::round_up(LARGE_ALLOC_SIZE, 8); |
| |
| // Allocate a single LARGE_ALLOC_SIZE chunk |
| MemTracker tracker(-1); |
| MemPool p1(&tracker); |
| uint8_t* ptr = p1.allocate(LARGE_ALLOC_SIZE); |
| EXPECT_TRUE(ptr != NULL); |
| EXPECT_EQ(int_max_rounded, p1.total_reserved_bytes()); |
| EXPECT_EQ(int_max_rounded, p1.total_allocated_bytes()); |
| p1.free_all(); |
| |
| // Allocate a small chunk (DEFAULT_INITIAL_CHUNK_SIZE) followed by an LARGE_ALLOC_SIZE chunk |
| MemPool p2(&tracker); |
| p2.allocate(8); |
| EXPECT_EQ(p2.total_reserved_bytes(), 4096); |
| EXPECT_EQ(p2.total_allocated_bytes(), 8); |
| ptr = p2.allocate(LARGE_ALLOC_SIZE); |
| EXPECT_TRUE(ptr != NULL); |
| EXPECT_EQ(p2.total_reserved_bytes(), 4096LL + int_max_rounded); |
| EXPECT_EQ(p2.total_allocated_bytes(), 8LL + int_max_rounded); |
| p2.free_all(); |
| |
| // Allocate three LARGE_ALLOC_SIZE chunks followed by a small chunk followed by another LARGE_ALLOC_SIZE |
| // chunk |
| MemPool p3(&tracker); |
| p3.allocate(LARGE_ALLOC_SIZE); |
| // Allocates new int_max_rounded * 2 chunk |
| // NOTE: exceed MAX_CHUNK_SIZE limit, will not *2 |
| ptr = p3.allocate(LARGE_ALLOC_SIZE); |
| EXPECT_TRUE(ptr != NULL); |
| EXPECT_EQ(int_max_rounded * 2, p3.total_reserved_bytes()); |
| EXPECT_EQ(int_max_rounded * 2, p3.total_allocated_bytes()); |
| // Uses existing int_max_rounded * 2 chunk |
| ptr = p3.allocate(LARGE_ALLOC_SIZE); |
| EXPECT_TRUE(ptr != NULL); |
| EXPECT_EQ(int_max_rounded * 3, p3.total_reserved_bytes()); |
| EXPECT_EQ(int_max_rounded * 3, p3.total_allocated_bytes()); |
| |
| // Allocates a new int_max_rounded * 4 chunk |
| // NOTE: exceed MAX_CHUNK_SIZE limit, will not *2 |
| #if !defined(ADDRESS_SANITIZER) || (__clang_major__ >= 3 && __clang_minor__ >= 7) |
| ptr = p3.allocate(8); |
| EXPECT_TRUE(ptr != NULL); |
| EXPECT_EQ(int_max_rounded * 3 + 512 * 1024, p3.total_reserved_bytes()); |
| EXPECT_EQ(int_max_rounded * 3 + 8, p3.total_allocated_bytes()); |
| // Uses existing int_max_rounded * 4 chunk |
| ptr = p3.allocate(LARGE_ALLOC_SIZE); |
| EXPECT_TRUE(ptr != NULL); |
| EXPECT_EQ(int_max_rounded * 4 + 512 * 1024, p3.total_reserved_bytes()); |
| EXPECT_EQ(int_max_rounded * 4 + 8, p3.total_allocated_bytes()); |
| #endif |
| p3.free_all(); |
| } |
| } // namespace doris |
| |
| int main(int argc, char** argv) { |
| // std::string conffile = std::string(getenv("DORIS_HOME")) + "/conf/be.conf"; |
| // if (!doris::config::init(conffile.c_str(), false)) { |
| // fprintf(stderr, "error read config file. \n"); |
| // return -1; |
| // } |
| doris::init_glog("be-test"); |
| |
| ::testing::InitGoogleTest(&argc, argv); |
| return RUN_ALL_TESTS(); |
| } |