Index: ps/trunk/source/lib/sysdep/arch/x86_x64/tests/test_topology.h =================================================================== --- ps/trunk/source/lib/sysdep/arch/x86_x64/tests/test_topology.h +++ ps/trunk/source/lib/sysdep/arch/x86_x64/tests/test_topology.h @@ -1,36 +0,0 @@ -/* Copyright (C) 2010 Wildfire Games. - * - * Permission is hereby granted, free of charge, to any person obtaining - * a copy of this software and associated documentation files (the - * "Software"), to deal in the Software without restriction, including - * without limitation the rights to use, copy, modify, merge, publish, - * distribute, sublicense, and/or sell copies of the Software, and to - * permit persons to whom the Software is furnished to do so, subject to - * the following conditions: - * - * The above copyright notice and this permission notice shall be included - * in all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. - * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY - * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, - * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE - * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - */ - -#include "lib/self_test.h" - -#include "lib/sysdep/arch/x86_x64/topology.h" - -class TestTopology : public CxxTest::TestSuite -{ -public: - void test_run() - { - TS_ASSERT_LESS_THAN_EQUALS(1u, topology::NumPackages()); - TS_ASSERT_LESS_THAN_EQUALS(1u, topology::CoresPerPackage()); - TS_ASSERT_LESS_THAN_EQUALS(1u, topology::LogicalPerCore()); - } -}; Index: ps/trunk/source/lib/sysdep/arch/x86_x64/topology.h =================================================================== --- ps/trunk/source/lib/sysdep/arch/x86_x64/topology.h +++ ps/trunk/source/lib/sysdep/arch/x86_x64/topology.h @@ -1,85 +0,0 @@ -/* Copyright (C) 2020 Wildfire Games. - * - * Permission is hereby granted, free of charge, to any person obtaining - * a copy of this software and associated documentation files (the - * "Software"), to deal in the Software without restriction, including - * without limitation the rights to use, copy, modify, merge, publish, - * distribute, sublicense, and/or sell copies of the Software, and to - * permit persons to whom the Software is furnished to do so, subject to - * the following conditions: - * - * The above copyright notice and this permission notice shall be included - * in all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. - * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY - * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, - * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE - * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - */ - -/* - * detection of CPU and cache topology. - * thread-safe, no explicit initialization is required. - */ - -#ifndef INCLUDED_X86_X64_TOPOLOGY -#define INCLUDED_X86_X64_TOPOLOGY - -#include "lib/sysdep/arch/x86_x64/apic.h" // ApicId - -namespace topology { - -//----------------------------------------------------------------------------- -// cpu - -// the CPU topology, i.e. how many packages, cores and logical processors are -// actually present and enabled, is useful for parameterizing parallel -// algorithms, especially on NUMA systems. -// -// note: OS abstractions usually only mention "processors", which could be -// any mix of the above. - -/** - * @return number of *enabled* CPU packages / sockets. - **/ -LIB_API size_t NumPackages(); - -/** - * @return number of *enabled* CPU cores per package. - * (2 on dual-core systems) - **/ -LIB_API size_t CoresPerPackage(); - -/** - * @return number of *enabled* logical processors (aka Hyperthreads) - * per core. (2 on P4 EE) - **/ -LIB_API size_t LogicalPerCore(); - -/** - * @return index of processor package/socket in [0, NumPackages()) - **/ -LIB_API size_t PackageFromApicId(ApicId apicId); - -/** - * @return index of processor core in [0, CoresPerPackage()) - **/ -LIB_API size_t CoreFromApicId(ApicId apicId); - -/** - * @return index of logical processor in [0, LogicalPerCore()) - **/ -LIB_API size_t LogicalFromApicId(ApicId apicId); - -/** - * @param idxPackage, idxCore, idxLogical return values of *FromApicId - * @return APIC ID (see note at AreApicIdsReliable) - **/ -LIB_API ApicId ApicIdFromIndices(size_t idxPackage, size_t idxCore, size_t idxLogical); - -} // namespace topology - -#endif // #ifndef INCLUDED_X86_X64_TOPOLOGY Index: ps/trunk/source/lib/sysdep/arch/x86_x64/topology.cpp =================================================================== --- ps/trunk/source/lib/sysdep/arch/x86_x64/topology.cpp +++ ps/trunk/source/lib/sysdep/arch/x86_x64/topology.cpp @@ -1,294 +0,0 @@ -/* Copyright (C) 2020 Wildfire Games. - * - * Permission is hereby granted, free of charge, to any person obtaining - * a copy of this software and associated documentation files (the - * "Software"), to deal in the Software without restriction, including - * without limitation the rights to use, copy, modify, merge, publish, - * distribute, sublicense, and/or sell copies of the Software, and to - * permit persons to whom the Software is furnished to do so, subject to - * the following conditions: - * - * The above copyright notice and this permission notice shall be included - * in all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. - * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY - * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, - * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE - * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - */ - -/* - * Detection of CPU topology - */ - -#include "precompiled.h" -#include "lib/sysdep/arch/x86_x64/topology.h" - -#include "lib/bits.h" -#include "lib/module_init.h" -#include "lib/sysdep/cpu.h" // ERR::CPU_FEATURE_MISSING -#include "lib/sysdep/os_cpu.h" -#include "lib/sysdep/numa.h" -#include "lib/sysdep/arch/x86_x64/x86_x64.h" -#include "lib/sysdep/arch/x86_x64/apic.h" - -#include -#include - -namespace topology { - -//--------------------------------------------------------------------------------------------------------------------- -// detect *maximum* number of cores/packages. -// note: some of them may be disabled by the OS or BIOS. -// note: Intel Appnote 485 assures us that they are uniform across packages. - -static size_t MaxCoresPerPackage() -{ - // assume single-core unless one of the following applies: - size_t maxCoresPerPackage = 1; - - x86_x64::CpuidRegs regs = { 0 }; - switch(x86_x64::Vendor()) - { - case x86_x64::VENDOR_INTEL: - regs.eax = 4; - regs.ecx = 0; - if(x86_x64::cpuid(®s)) - maxCoresPerPackage = bits(regs.eax, 26, 31)+1; - break; - - case x86_x64::VENDOR_AMD: - regs.eax = 0x80000008; - if(x86_x64::cpuid(®s)) - maxCoresPerPackage = bits(regs.ecx, 0, 7)+1; - break; - - default: - break; - } - - return maxCoresPerPackage; -} - - -static size_t MaxLogicalPerCore() -{ - struct IsHyperthreadingCapable - { - bool operator()() const - { - // definitely not - if(!x86_x64::Cap(x86_x64::CAP_HT)) - return false; - - // multi-core AMD systems falsely set the HT bit for reasons of - // compatibility. we'll just ignore it, because clearing it might - // confuse other callers. - if(x86_x64::Vendor() == x86_x64::VENDOR_AMD && x86_x64::Cap(x86_x64::CAP_AMD_CMP_LEGACY)) - return false; - - return true; - } - }; - if(IsHyperthreadingCapable()()) - { - x86_x64::CpuidRegs regs = { 0 }; - regs.eax = 1; - if(!x86_x64::cpuid(®s)) - DEBUG_WARN_ERR(ERR::CPU_FEATURE_MISSING); - const size_t logicalPerPackage = bits(regs.ebx, 16, 23); - const size_t maxCoresPerPackage = MaxCoresPerPackage(); - // cores ought to be uniform WRT # logical processors - ENSURE(logicalPerPackage % maxCoresPerPackage == 0); - const size_t maxLogicalPerCore = logicalPerPackage / maxCoresPerPackage; - return maxLogicalPerCore; - } - else - return 1; -} - -//--------------------------------------------------------------------------------------------------------------------- -// CPU topology interface - -// APIC IDs consist of variable-length bit fields indicating the logical, -// core, package and cache IDs. Vol3a says they aren't guaranteed to be -// contiguous, but that also applies to the individual fields. -// for example, quad-core E5630 CPUs report 4-bit core IDs 0, 1, 6, 7. -struct ApicField // POD -{ - size_t operator()(size_t bits) const - { - return (bits >> shift) & mask; - } - - size_t mask; // zero for zero-width fields - size_t shift; -}; - -struct CpuTopology // POD -{ - size_t numProcessors; // total reported by OS - - ApicField logical; - ApicField core; - ApicField package; - - // how many are actually enabled - size_t logicalPerCore; - size_t coresPerPackage; - size_t numPackages; -}; -static CpuTopology cpuTopology; -static ModuleInitState cpuInitState; - -static Status InitCpuTopology() -{ - cpuTopology.numProcessors = os_cpu_NumProcessors(); - - const size_t maxLogicalPerCore = MaxLogicalPerCore(); - const size_t maxCoresPerPackage = MaxCoresPerPackage(); - const size_t maxPackages = 256; // "enough" - - const size_t logicalWidth = ceil_log2(maxLogicalPerCore); - const size_t coreWidth = ceil_log2(maxCoresPerPackage); - const size_t packageWidth = ceil_log2(maxPackages); - - cpuTopology.logical.mask = bit_mask(logicalWidth); - cpuTopology.core.mask = bit_mask(coreWidth); - cpuTopology.package.mask = bit_mask(packageWidth); - - cpuTopology.logical.shift = 0; - cpuTopology.core.shift = logicalWidth; - cpuTopology.package.shift = logicalWidth + coreWidth; - - if(AreApicIdsReliable()) - { - struct NumUniqueValuesInField - { - size_t operator()(const ApicField& apicField) const - { - std::bitset values; - for(size_t processor = 0; processor < os_cpu_NumProcessors(); processor++) - { - const ApicId apicId = ApicIdFromProcessor(processor); - const size_t value = apicField(apicId); - values.set(value); - } - return values.count(); - } - }; - - cpuTopology.logicalPerCore = NumUniqueValuesInField()(cpuTopology.logical); - cpuTopology.coresPerPackage = NumUniqueValuesInField()(cpuTopology.core); - cpuTopology.numPackages = NumUniqueValuesInField()(cpuTopology.package); - } - else // processor lacks an xAPIC, or IDs are invalid - { - struct MinPackages - { - size_t operator()(size_t maxCoresPerPackage, size_t maxLogicalPerCore) const - { - const size_t numNodes = numa_NumNodes(); - const size_t logicalPerNode = PopulationCount(numa_ProcessorMaskFromNode(0)); - // NB: some cores or logical processors may be disabled. - const size_t maxLogicalPerPackage = maxCoresPerPackage*maxLogicalPerCore; - const size_t minPackagesPerNode = DivideRoundUp(logicalPerNode, maxLogicalPerPackage); - return minPackagesPerNode*numNodes; - } - }; - - // we can't differentiate between cores and logical processors. - // since the former are less likely to be disabled, we seek the - // maximum feasible number of cores and minimal number of packages: - const size_t minPackages = MinPackages()(maxCoresPerPackage, maxLogicalPerCore); - for(size_t numPackages = minPackages; numPackages <= cpuTopology.numProcessors; numPackages++) - { - if(cpuTopology.numProcessors % numPackages != 0) - continue; - const size_t logicalPerPackage = cpuTopology.numProcessors / numPackages; - const size_t minCoresPerPackage = DivideRoundUp(logicalPerPackage, maxLogicalPerCore); - for(size_t coresPerPackage = maxCoresPerPackage; coresPerPackage >= minCoresPerPackage; coresPerPackage--) - { - if(logicalPerPackage % coresPerPackage != 0) - continue; - const size_t logicalPerCore = logicalPerPackage / coresPerPackage; - if(logicalPerCore <= maxLogicalPerCore) - { - ENSURE(cpuTopology.numProcessors == numPackages*coresPerPackage*logicalPerCore); - cpuTopology.logicalPerCore = logicalPerCore; - cpuTopology.coresPerPackage = coresPerPackage; - cpuTopology.numPackages = numPackages; - - return INFO::OK; - } - } - } - - DEBUG_WARN_ERR(ERR::LOGIC); // didn't find a feasible topology - } - - return INFO::OK; -} - - -size_t NumPackages() -{ - ModuleInit(&cpuInitState, InitCpuTopology); - return cpuTopology.numPackages; -} - -size_t CoresPerPackage() -{ - ModuleInit(&cpuInitState, InitCpuTopology); - return cpuTopology.coresPerPackage; -} - -size_t LogicalPerCore() -{ - ModuleInit(&cpuInitState, InitCpuTopology); - return cpuTopology.logicalPerCore; -} - -size_t LogicalFromApicId(ApicId apicId) -{ - const size_t contiguousId = ContiguousIdFromApicId(apicId); - return contiguousId % cpuTopology.logicalPerCore; -} - -size_t CoreFromApicId(ApicId apicId) -{ - const size_t contiguousId = ContiguousIdFromApicId(apicId); - return (contiguousId / cpuTopology.logicalPerCore) % cpuTopology.coresPerPackage; -} - -size_t PackageFromApicId(ApicId apicId) -{ - const size_t contiguousId = ContiguousIdFromApicId(apicId); - return contiguousId / (cpuTopology.logicalPerCore * cpuTopology.coresPerPackage); -} - - -ApicId ApicIdFromIndices(size_t idxLogical, size_t idxCore, size_t idxPackage) -{ - ModuleInit(&cpuInitState, InitCpuTopology); - - size_t contiguousId = 0; - ENSURE(idxPackage < cpuTopology.numPackages); - contiguousId += idxPackage; - - contiguousId *= cpuTopology.coresPerPackage; - ENSURE(idxCore < cpuTopology.coresPerPackage); - contiguousId += idxCore; - - contiguousId *= cpuTopology.logicalPerCore; - ENSURE(idxLogical < cpuTopology.logicalPerCore); - contiguousId += idxLogical; - - ENSURE(contiguousId < cpuTopology.numProcessors); - return ApicIdFromContiguousId(contiguousId); -} - -} // namespace topology Index: ps/trunk/source/ps/GameSetup/HWDetect.cpp =================================================================== --- ps/trunk/source/ps/GameSetup/HWDetect.cpp +++ ps/trunk/source/ps/GameSetup/HWDetect.cpp @@ -1,4 +1,4 @@ -/* Copyright (C) 2021 Wildfire Games. +/* Copyright (C) 2022 Wildfire Games. * This file is part of 0 A.D. * * 0 A.D. is free software: you can redistribute it and/or modify @@ -25,9 +25,6 @@ #include "lib/sysdep/cpu.h" #include "lib/sysdep/numa.h" #include "lib/sysdep/os_cpu.h" -#if ARCH_X86_X64 -# include "lib/sysdep/arch/x86_x64/topology.h" -#endif #if CONFIG2_AUDIO #include "soundmanager/SoundManager.h" #endif @@ -177,11 +174,6 @@ Script::SetProperty(rq, settings, "cpu_pagesize", (u32)os_cpu_PageSize()); Script::SetProperty(rq, settings, "cpu_largepagesize", (u32)os_cpu_LargePageSize()); Script::SetProperty(rq, settings, "cpu_numprocs", (u32)os_cpu_NumProcessors()); -#if ARCH_X86_X64 - Script::SetProperty(rq, settings, "cpu_numpackages", (u32)topology::NumPackages()); - Script::SetProperty(rq, settings, "cpu_coresperpackage", (u32)topology::CoresPerPackage()); - Script::SetProperty(rq, settings, "cpu_logicalpercore", (u32)topology::LogicalPerCore()); -#endif Script::SetProperty(rq, settings, "numa_numnodes", (u32)numa_NumNodes()); Script::SetProperty(rq, settings, "numa_factor", numa_Factor()); Index: ps/trunk/source/ps/Util.cpp =================================================================== --- ps/trunk/source/ps/Util.cpp +++ ps/trunk/source/ps/Util.cpp @@ -1,4 +1,4 @@ -/* Copyright (C) 2021 Wildfire Games. +/* Copyright (C) 2022 Wildfire Games. * This file is part of 0 A.D. * * 0 A.D. is free software: you can redistribute it and/or modify @@ -25,9 +25,6 @@ #include "lib/bits.h" // round_up #include "lib/ogl.h" #include "lib/posix/posix_utsname.h" -#if ARCH_X86_X64 -#include "lib/sysdep/arch/x86_x64/topology.h" -#endif #include "lib/sysdep/cpu.h" #include "lib/sysdep/os_cpu.h" #include "lib/sysdep/smbios.h" @@ -81,9 +78,6 @@ // CPU fprintf(f, "CPU : %s, %s", un.machine, cpu_IdentifierString()); -#if ARCH_X86_X64 - fprintf(f, " (%dx%dx%d)", (int)topology::NumPackages(), (int)topology::CoresPerPackage(), (int)topology::LogicalPerCore()); -#endif double cpuClock = os_cpu_ClockFrequency(); // query OS (may fail) #if ARCH_X86_X64 if(cpuClock <= 0.0)