// ---------------------------------------------------------------------- // File: CoreLocalArray.hh // Author: Georgios Bitzes - CERN // ---------------------------------------------------------------------- /************************************************************************ * quarkdb - a redis-like highly available key-value store * * Copyright (C) 2019 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 .* ************************************************************************/ #ifndef QUARKDB_CORE_LOCAL_ARRAY_HH #define QUARKDB_CORE_LOCAL_ARRAY_HH #include #include #include #include #include //------------------------------------------------------------------------------ // Implementation largely inspired from rocksdb/util/core_local.h // // Copyright (c) 2017-present, Facebook, Inc. All rights reserved. // This source code is licensed under both the GPLv2 (found in the // COPYING file in the root directory) and Apache 2.0 License // (found in the LICENSE.Apache file in the root directory). //------------------------------------------------------------------------------ namespace quarkdb { namespace CoreLocal { // TODO: Replace with std::hardware_destructive_interference_size // once supported by the compiler. constexpr size_t kCacheLine = 64; }; //------------------------------------------------------------------------------ // An array in which each CPU core "owns" one of the elements. // T must be cache-line aligned to prevent false sharing during writes. //------------------------------------------------------------------------------ template class CoreLocalArray { public: //---------------------------------------------------------------------------- // Constructor //---------------------------------------------------------------------------- CoreLocalArray() : cpus(std::thread::hardware_concurrency()), array(nullptr, free) { static_assert(sizeof(T) % CoreLocal::kCacheLine == 0, "CoreLocalArray only makes sense for cache-line aligned types"); array = { reinterpret_cast(aligned_alloc(CoreLocal::kCacheLine, cpus*sizeof(T))), std::free }; // Default-construct elements for(size_t i = 0; i < cpus; i++) { new (array.get() + i) T(); } } //---------------------------------------------------------------------------- // Destructor //---------------------------------------------------------------------------- ~CoreLocalArray() { // Call destructors for(size_t i = 0; i < cpus; i++) { (array.get())[i].~T(); } } //---------------------------------------------------------------------------- // Get size of the array //---------------------------------------------------------------------------- size_t size() const { return cpus; } //---------------------------------------------------------------------------- // Access i-th element of the array, no matter which core we're executing at //---------------------------------------------------------------------------- T* accessAtCore(size_t index) { return array.get() + index; } //---------------------------------------------------------------------------- // Access i-th element of the array, no matter which core we're executing at. // Const overload. //---------------------------------------------------------------------------- const T* accessAtCore(size_t index) const { return array.get() + index; } //---------------------------------------------------------------------------- // Return the core index we would have used when calling access() //---------------------------------------------------------------------------- int getCoreIndex() const { int cpuno = sched_getcpu(); if(cpuno < 0 || cpuno > (int) cpus) { cpuno = 0; } return cpuno; } //---------------------------------------------------------------------------- // Access element specific to the core we're currently running at, and return // our core index. //---------------------------------------------------------------------------- std::pair access() { int cpuno = getCoreIndex(); return {accessAtCore(cpuno), cpuno}; } private: size_t cpus; std::unique_ptr array; }; } #endif