| /* | |
| * Copyright (c) 2021, 2025, Oracle and/or its affiliates. All rights reserved. | |
| * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. | |
| * | |
| * This code is free software; you can redistribute it and/or modify it | |
| * under the terms of the GNU General Public License version 2 only, as | |
| * published by the Free Software Foundation. | |
| * | |
| * This code 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 | |
| * version 2 for more details (a copy is included in the LICENSE file that | |
| * accompanied this code). | |
| * | |
| * You should have received a copy of the GNU General Public License version | |
| * 2 along with this work; if not, write to the Free Software Foundation, | |
| * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. | |
| * | |
| * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA | |
| * or visit www.oracle.com if you need additional information or have any | |
| * questions. | |
| */ | |
| #ifndef SHARE_GC_Z_ZINDEXDISTRIBUTOR_INLINE_HPP | |
| #define SHARE_GC_Z_ZINDEXDISTRIBUTOR_INLINE_HPP | |
| #include "gc/z/zIndexDistributor.hpp" | |
| #include "gc/shared/gc_globals.hpp" | |
| #include "gc/z/zGlobals.hpp" | |
| #include "runtime/atomic.hpp" | |
| #include "runtime/os.hpp" | |
| #include "runtime/thread.hpp" | |
| #include "utilities/align.hpp" | |
| #include "utilities/powerOfTwo.hpp" | |
| class ZIndexDistributorStriped : public CHeapObj<mtGC> { | |
| static const int MemSize = 4096; | |
| static const int StripeCount = MemSize / ZCacheLineSize; | |
| const int _count; | |
| // For claiming a stripe | |
| volatile int _claim_stripe; | |
| // For claiming inside a stripe | |
| char _mem[MemSize + ZCacheLineSize]; | |
| int claim_stripe() { | |
| return Atomic::fetch_then_add(&_claim_stripe, 1, memory_order_relaxed); | |
| } | |
| volatile int* claim_addr(int index) { | |
| return (volatile int*)(align_up(_mem, ZCacheLineSize) + (size_t)index * ZCacheLineSize); | |
| } | |
| public: | |
| ZIndexDistributorStriped(int count) | |
| : _count(count), | |
| _claim_stripe(0), | |
| _mem() { | |
| memset(_mem, 0, MemSize + ZCacheLineSize); | |
| } | |
| template <typename Function> | |
| void do_indices(Function function) { | |
| const int stripe_max = _count / StripeCount; | |
| // Use claiming | |
| for (int i; (i = claim_stripe()) < StripeCount;) { | |
| for (int index; (index = Atomic::fetch_then_add(claim_addr(i), 1, memory_order_relaxed)) < stripe_max;) { | |
| if (!function(i * stripe_max + index)) { | |
| return; | |
| } | |
| } | |
| } | |
| // Use stealing | |
| for (int i = 0; i < StripeCount; i++) { | |
| for (int index; (index = Atomic::fetch_then_add(claim_addr(i), 1, memory_order_relaxed)) < stripe_max;) { | |
| if (!function(i * stripe_max + index)) { | |
| return; | |
| } | |
| } | |
| } | |
| } | |
| static size_t get_count(size_t max_count) { | |
| // Must be multiple of the StripeCount | |
| return align_up(max_count, StripeCount); | |
| } | |
| }; | |
| class ZIndexDistributorClaimTree : public CHeapObj<mtGC> { | |
| friend class ZIndexDistributorTest; | |
| private: | |
| // The N - 1 levels are used to claim a segment in the | |
| // next level the Nth level claims an index. | |
| static constexpr int N = 4; | |
| static constexpr int ClaimLevels = N - 1; | |
| // Describes the how the number of indices increases when going up from the given level | |
| static constexpr int level_multiplier(int level) { | |
| assert(level < ClaimLevels, "Must be"); | |
| constexpr int array[ClaimLevels]{16, 16, 16}; | |
| return array[level]; | |
| } | |
| // Number of indices in one segment at the last level | |
| const int _last_level_segment_size_shift; | |
| // For deallocation | |
| char* _malloced; | |
| // Contains the tree of claim variables | |
| volatile int* _claim_array; | |
| // Claim index functions | |
| // Number of claim entries at the given level | |
| static constexpr int claim_level_size(int level) { | |
| if (level == 0) { | |
| return 1; | |
| } | |
| return level_multiplier(level - 1) * claim_level_size(level - 1); | |
| } | |
| // The index the next level starts at | |
| static constexpr int claim_level_end_index(int level) { | |
| if (level == 0) { | |
| // First level uses padding | |
| return ZCacheLineSize / sizeof(int); | |
| } | |
| return claim_level_size(level) + claim_level_end_index(level - 1); | |
| } | |
| static constexpr int claim_level_start_index(int level) { | |
| return claim_level_end_index(level - 1); | |
| } | |
| // Total size used to hold all claim variables | |
| static size_t claim_variables_size() { | |
| return sizeof(int) * (size_t)claim_level_end_index(ClaimLevels); | |
| } | |
| // Returns the index of the start of the current segment of the current level | |
| static constexpr int claim_level_index_accumulate(int* indices, int level, int acc = 1) { | |
| if (level == 0) { | |
| return acc * indices[level]; | |
| } | |
| return acc * indices[level] + claim_level_index_accumulate(indices, level - 1, acc * level_multiplier(level)); | |
| } | |
| static constexpr int claim_level_index(int* indices, int level) { | |
| assert(level > 0, "Must be"); | |
| // The claim index for the current level is found in the previous levels | |
| return claim_level_index_accumulate(indices, level - 1); | |
| } | |
| static constexpr int claim_index(int* indices, int level) { | |
| if (level == 0) { | |
| return 0; | |
| } | |
| return claim_level_start_index(level) + claim_level_index(indices, level); | |
| } | |
| // Claim functions | |
| int claim(int index) { | |
| return Atomic::fetch_then_add(&_claim_array[index], 1, memory_order_relaxed); | |
| } | |
| int claim_at(int* indices, int level) { | |
| const int index = claim_index(indices, level); | |
| const int value = claim(index); | |
| #if 0 | |
| if (level == 0) { tty->print_cr("Claim at: %d index: %d got: %d", indices[0], index, value); } | |
| else if (level == 1) { tty->print_cr("Claim at: %d %d index: %d got: %d", indices[0], indices[1], index, value); } | |
| else if (level == 2) { tty->print_cr("Claim at: %d %d %d index: %d got: %d", indices[0], indices[1], indices[2], index, value); } | |
| else if (level == 3) { tty->print_cr("Claim at: %d %d %d %d index: %d got: %d", indices[0], indices[1], indices[2], indices[3], index, value); } | |
| else if (level == 4) { tty->print_cr("Claim at: %d %d %d %d %d index: %d got: %d", indices[0], indices[1], indices[2], indices[3], indices[4], index, value); } | |
| #endif | |
| return value; | |
| } | |
| template <typename Function> | |
| void claim_and_do(Function function, int* indices, int level) { | |
| if (level < N) { | |
| // Visit ClaimLevels and the last level | |
| const int ci = claim_index(indices, level); | |
| for (indices[level] = 0; (indices[level] = claim(ci)) < level_segment_size(level);) { | |
| claim_and_do(function, indices, level + 1); | |
| } | |
| return; | |
| } | |
| doit(function, indices); | |
| } | |
| template <typename Function> | |
| void steal_and_do(Function function, int* indices, int level) { | |
| for (indices[level] = 0; indices[level] < level_segment_size(level); indices[level]++) { | |
| const int next_level = level + 1; | |
| // First try to claim at next level | |
| claim_and_do(function, indices, next_level); | |
| // Then steal at next level | |
| if (next_level < ClaimLevels) { | |
| steal_and_do(function, indices, next_level); | |
| } | |
| } | |
| } | |
| // Functions to claimed values to an index | |
| static constexpr int levels_size(int level) { | |
| if (level == 0) { | |
| return level_multiplier(0); | |
| } | |
| return level_multiplier(level) * levels_size(level - 1); | |
| } | |
| static int constexpr level_to_last_level_count_coverage(int level) { | |
| return levels_size(ClaimLevels - 1) / levels_size(level); | |
| } | |
| static int constexpr calculate_last_level_count(int* indices, int level = 0) { | |
| if (level == N - 1) { | |
| return 0; | |
| } | |
| return indices[level] * level_to_last_level_count_coverage(level) + calculate_last_level_count(indices, level + 1); | |
| } | |
| int calculate_index(int* indices) { | |
| const int segment_start = calculate_last_level_count(indices) << _last_level_segment_size_shift; | |
| return segment_start + indices[N - 1]; | |
| } | |
| int level_segment_size(int level) { | |
| if (level == ClaimLevels) { | |
| return 1 << _last_level_segment_size_shift; | |
| } | |
| return level_multiplier(level); | |
| } | |
| template <typename Function> | |
| void doit(Function function, int* indices) { | |
| //const int index = first_level * second_level_max * _third_level_max + second_level * _third_level_max + third_level; | |
| const int index = calculate_index(indices); | |
| #if 0 | |
| tty->print_cr("doit Thread: " PTR_FORMAT ": %d %d %d %d => %d", | |
| p2i(Thread::current()), | |
| indices[0], indices[1], indices[2], indices[3], index); | |
| #endif | |
| function(index); | |
| } | |
| static int last_level_segment_size_shift(int count) { | |
| const int last_level_size = count / levels_size(ClaimLevels - 1); | |
| assert(levels_size(ClaimLevels - 1) * last_level_size == count, "Not exactly divisible"); | |
| return log2i_exact(last_level_size); | |
| } | |
| public: | |
| ZIndexDistributorClaimTree(int count) | |
| : _last_level_segment_size_shift(last_level_segment_size_shift(count)), | |
| _malloced((char*)os::malloc(claim_variables_size() + os::vm_page_size(), mtGC)), | |
| _claim_array((volatile int*)align_up(_malloced, os::vm_page_size())) { | |
| assert((levels_size(ClaimLevels - 1) << _last_level_segment_size_shift) == count, "Incorrectly setup"); | |
| #if 0 | |
| tty->print_cr("ZIndexDistributorClaimTree count: %d byte size: %zu", count, claim_variables_size() + os::vm_page_size()); | |
| #endif | |
| memset(_malloced, 0, claim_variables_size() + os::vm_page_size()); | |
| } | |
| ~ZIndexDistributorClaimTree() { | |
| os::free(_malloced); | |
| } | |
| template <typename Function> | |
| void do_indices(Function function) { | |
| int indices[N]; | |
| claim_and_do(function, indices, 0 /* level */); | |
| steal_and_do(function, indices, 0 /* level */); | |
| } | |
| static size_t get_count(size_t max_count) { | |
| // Must be at least claim_level_size(ClaimLevels) and a power of two | |
| const size_t min_count = claim_level_size(ClaimLevels); | |
| return round_up_power_of_2(MAX2(max_count, min_count)); | |
| } | |
| }; | |
| // Using dynamically allocated objects just to be able to evaluate | |
| // different strategies. Revert when one has been choosen. | |
| inline void* ZIndexDistributor::create_strategy(int count) { | |
| switch (ZIndexDistributorStrategy) { | |
| case 0: return new ZIndexDistributorClaimTree(count); | |
| case 1: return new ZIndexDistributorStriped(count); | |
| default: fatal("Unknown ZIndexDistributorStrategy"); return nullptr; | |
| }; | |
| } | |
| inline ZIndexDistributor::ZIndexDistributor(int count) | |
| : _strategy(create_strategy(count)) {} | |
| inline ZIndexDistributor::~ZIndexDistributor() { | |
| switch (ZIndexDistributorStrategy) { | |
| case 0: delete static_cast<ZIndexDistributorClaimTree*>(_strategy); break; | |
| case 1: delete static_cast<ZIndexDistributorStriped*>(_strategy); break; | |
| default: fatal("Unknown ZIndexDistributorStrategy"); break; | |
| }; | |
| } | |
| template <typename Strategy> | |
| inline Strategy* ZIndexDistributor::strategy() { | |
| return static_cast<Strategy*>(_strategy); | |
| } | |
| template <typename Function> | |
| inline void ZIndexDistributor::do_indices(Function function) { | |
| switch (ZIndexDistributorStrategy) { | |
| case 0: strategy<ZIndexDistributorClaimTree>()->do_indices(function); break; | |
| case 1: strategy<ZIndexDistributorStriped>()->do_indices(function); break; | |
| default: fatal("Unknown ZIndexDistributorStrategy"); | |
| }; | |
| } | |
| inline size_t ZIndexDistributor::get_count(size_t max_count) { | |
| size_t required_count; | |
| switch (ZIndexDistributorStrategy) { | |
| case 0: required_count = ZIndexDistributorClaimTree::get_count(max_count); break; | |
| case 1: required_count = ZIndexDistributorStriped::get_count(max_count); break; | |
| default: fatal("Unknown ZIndexDistributorStrategy"); | |
| }; | |
| assert(max_count <= required_count, "unsupported max_count: %zu", max_count); | |
| return required_count; | |
| } | |
| #endif // SHARE_GC_Z_ZINDEXDISTRIBUTOR_INLINE_HPP |