From fe5c9c82e03cecaf2ef9aa3aec3cbeca681b44bb Mon Sep 17 00:00:00 2001 From: Chris Blume Date: Mon, 6 Oct 2025 21:47:34 -0400 Subject: [PATCH 1/2] Report core performance/efficiency This commit reports the different efficiency levels of the various CPU cores. It also changes the worker threads count to match the highest performance core count. This contributes to (and possibly closes) #3 --- Code/EntryPoint.cpp | 26 ++++-- Code/GetProcessorCoreInformation.cpp | 60 +++++++++++++ Code/GetProcessorCoreInformation.hpp | 20 +++++ Code/GetProcessorInformation.cpp | 90 ------------------- Code/GetProcessorInformation.hpp | 14 --- .../VisualStudio/FileReadSpeedTest.vcxproj | 4 +- 6 files changed, 100 insertions(+), 114 deletions(-) create mode 100644 Code/GetProcessorCoreInformation.cpp create mode 100644 Code/GetProcessorCoreInformation.hpp delete mode 100644 Code/GetProcessorInformation.cpp delete mode 100644 Code/GetProcessorInformation.hpp diff --git a/Code/EntryPoint.cpp b/Code/EntryPoint.cpp index d1b398a..9021d75 100644 --- a/Code/EntryPoint.cpp +++ b/Code/EntryPoint.cpp @@ -1,6 +1,7 @@ +#include #include -#include "GetProcessorInformation.hpp" +#include "GetProcessorCoreInformation.hpp" #include "OverlappedIOFileRead.hpp" int main(int argc, const char * argv[]) { @@ -10,19 +11,28 @@ int main(int argc, const char * argv[]) { return -1; } - auto processor_information = GetProcessorInformation(); - if (!processor_information.has_value()) { + auto processor_core_information = GetProcessorCoreInformation(); + if (!processor_core_information.has_value()) { std::cerr << "Error: Could not find processor information." << std::endl; return -1; } - std::cout << "CPU cores: " << processor_information->actual_cores_; - if (processor_information->hyperthreading_cores_ != 0) { - std::cout << " (and " << processor_information->hyperthreading_cores_ << " hyperthreading cores)"; + int16_t max_efficiency = -1; + uint16_t max_efficiency_core_count = 0; + for (auto& core_information : *processor_core_information) { + std::cout << "CPU cores: " << core_information.count_ << " @ performance class: " << core_information.efficiency_class_; + if (core_information.has_hyperthreading_ != 0) { + std::cout << " (with hyperthreading)"; + } + + if (core_information.efficiency_class_ > max_efficiency) { + max_efficiency = core_information.efficiency_class_; + max_efficiency_core_count = core_information.count_; + } + std::cout << std::endl; } - std::cout << std::endl; - auto overlapped_io_file_read = PrepareToReadFile(argv[1], processor_information->actual_cores_); + auto overlapped_io_file_read = PrepareToReadFile(argv[1], max_efficiency_core_count); if (!overlapped_io_file_read.has_value()) { std::cerr << "Error"; return -1; diff --git a/Code/GetProcessorCoreInformation.cpp b/Code/GetProcessorCoreInformation.cpp new file mode 100644 index 0000000..068cd79 --- /dev/null +++ b/Code/GetProcessorCoreInformation.cpp @@ -0,0 +1,60 @@ +#include "GetProcessorCoreInformation.hpp" + +#ifndef WIN32_LEAN_AND_MEAN +#define WIN32_LEAN_AND_MEAN +#endif +#include + +#include +#include + +ProcessorCoreInformation::ProcessorCoreInformation(uint16_t efficiency_class, uint16_t count, bool has_hyperthreading) noexcept + : efficiency_class_(std::move(efficiency_class)) + , count_(std::move(count)) + , has_hyperthreading_(std::move(has_hyperthreading)) +{} + +std::optional> GetProcessorCoreInformation() noexcept { + DWORD required_buffer_size = 0; + BOOL result = GetLogicalProcessorInformationEx(RelationProcessorCore, nullptr, &required_buffer_size); + if (result == FALSE) { + DWORD error_code = GetLastError(); + if (error_code != ERROR_INSUFFICIENT_BUFFER) { + return std::nullopt; + } + } + + const size_t element_size = sizeof(SYSTEM_LOGICAL_PROCESSOR_INFORMATION_EX); + // char[] because chars enable aliasing + auto buffer = std::make_unique(required_buffer_size); + auto buffer_end = buffer.get() + required_buffer_size; + auto* current_pointer = buffer.get(); + auto* processor_information = (SYSTEM_LOGICAL_PROCESSOR_INFORMATION_EX*)current_pointer; + result = GetLogicalProcessorInformationEx(RelationProcessorCore, processor_information, &required_buffer_size); + if (result == FALSE) { + return std::nullopt; + } + + std::vector core_informations; + for (size_t i = 0; (char*)current_pointer < buffer_end; i++) { + uint32_t efficiency = processor_information->Processor.EfficiencyClass; + bool hyperthreading = processor_information->Processor.Flags == LTP_PC_SMT; + + bool match_found = false; + for (auto& core_information : core_informations) { + if (core_information.efficiency_class_ == efficiency && + core_information.has_hyperthreading_ == hyperthreading) { + match_found = true; + core_information.count_++; + } + } + if (!match_found) { + core_informations.emplace_back(efficiency, 1, hyperthreading); + } + + current_pointer += processor_information->Size; + processor_information = (SYSTEM_LOGICAL_PROCESSOR_INFORMATION_EX*)current_pointer; + } + + return core_informations; +} \ No newline at end of file diff --git a/Code/GetProcessorCoreInformation.hpp b/Code/GetProcessorCoreInformation.hpp new file mode 100644 index 0000000..5940789 --- /dev/null +++ b/Code/GetProcessorCoreInformation.hpp @@ -0,0 +1,20 @@ +#ifndef GETPROCESSORCOREINFORMATION_HPP +#define GETPROCESSORCOREINFORMATION_HPP + +#include +#include +#include + +struct ProcessorCoreInformation { +public: + + explicit ProcessorCoreInformation(uint16_t efficiency_class, uint16_t count, bool has_hyperthreading) noexcept; + + uint16_t efficiency_class_; + uint16_t count_; + bool has_hyperthreading_; +}; + +std::optional> GetProcessorCoreInformation() noexcept; + +#endif // #ifndef GETPROCESSORCOREINFORMATION_HPP \ No newline at end of file diff --git a/Code/GetProcessorInformation.cpp b/Code/GetProcessorInformation.cpp deleted file mode 100644 index e2169d7..0000000 --- a/Code/GetProcessorInformation.cpp +++ /dev/null @@ -1,90 +0,0 @@ -#include "GetProcessorInformation.hpp" - -#ifndef WIN32_LEAN_AND_MEAN -#define WIN32_LEAN_AND_MEAN -#endif -#include - -#include - -//#include - -/*struct ProcessorInformation { - int actual_cores_; - int hyperthreading_cores_; -};*/ - -std::optional GetProcessorInformation() noexcept { - DWORD required_buffer_size = 0; - BOOL result = GetLogicalProcessorInformation(nullptr, &required_buffer_size); - if (result == FALSE) { - DWORD error_code = GetLastError(); - if (error_code != ERROR_INSUFFICIENT_BUFFER) { - return std::nullopt; - } - } - size_t processor_information_count = required_buffer_size / sizeof(SYSTEM_LOGICAL_PROCESSOR_INFORMATION); - auto processor_information = std::make_unique(processor_information_count); - result = GetLogicalProcessorInformation(processor_information.get(), &required_buffer_size); - if (result == FALSE) { - return std::nullopt; - } - - ProcessorCoreInformation processor_core_information = {}; - - for(DWORD i = 0; i < processor_information_count; i++) { - //std::cout << "Core " << i << ": " << processor_information[i].ProcessorMask << std::endl; - switch (processor_information[i].Relationship) { - /* - case RelationProcessorPackage: - std::cout << "\tPhysical package" << std::endl; - break; - case RelationNumaNode: - std::cout << "\tNUMA node: " << processor_information[i].NumaNode.NodeNumber << std::endl; - break; - */ - case RelationProcessorCore: - { - processor_core_information.actual_cores_++; - if (processor_information[i].ProcessorCore.Flags == 1) { - processor_core_information.hyperthreading_cores_++; - } - } - break; - /* - case RelationCache: - std::cout << "\tL"; - switch (processor_information[i].Cache.Level) { - case 1: - std::cout << "1 "; - break; - case 2: - std::cout << "2 "; - break; - case 3: - std::cout << "3 "; - break; - } - switch (processor_information[i].Cache.Type) { - case CacheUnified: - std::cout << "unified"; - break; - case CacheInstruction: - std::cout << "instuction"; - break; - case CacheData: - std::cout << "data"; - break; - case CacheTrace: - std::cout << "trace"; - break; - } - std::cout << " cache, " << processor_information[i].Cache.Size << " KiB" << std::endl; - std::cout << "\tCache line size: " << processor_information[i].Cache.LineSize << " KiB" << std::endl; - break; - */ - } - } - - return processor_core_information; -} \ No newline at end of file diff --git a/Code/GetProcessorInformation.hpp b/Code/GetProcessorInformation.hpp deleted file mode 100644 index c42f35e..0000000 --- a/Code/GetProcessorInformation.hpp +++ /dev/null @@ -1,14 +0,0 @@ -#ifndef GETPROCESSORINFORMATION_HPP -#define GETPROCESSORINFORMATION_HPP - -#include - -class ProcessorCoreInformation { -public: - int actual_cores_; - int hyperthreading_cores_; -}; - -std::optional GetProcessorInformation() noexcept; - -#endif // #ifndef GETPROCESSORINFORMATION_HPP \ No newline at end of file diff --git a/Projects/VisualStudio/FileReadSpeedTest.vcxproj b/Projects/VisualStudio/FileReadSpeedTest.vcxproj index 63dd2b8..7ecf7cb 100644 --- a/Projects/VisualStudio/FileReadSpeedTest.vcxproj +++ b/Projects/VisualStudio/FileReadSpeedTest.vcxproj @@ -20,13 +20,13 @@ - + - + From 50c4c24a6b77c6762b4d29600ef33a113d51c565 Mon Sep 17 00:00:00 2001 From: Chris Blume Date: Tue, 7 Oct 2025 10:41:40 -0400 Subject: [PATCH 2/2] Add copyright notice --- Code/GetProcessorCoreInformation.cpp | 4 ++++ Code/GetProcessorCoreInformation.hpp | 4 ++++ 2 files changed, 8 insertions(+) diff --git a/Code/GetProcessorCoreInformation.cpp b/Code/GetProcessorCoreInformation.cpp index 068cd79..9c965c9 100644 --- a/Code/GetProcessorCoreInformation.cpp +++ b/Code/GetProcessorCoreInformation.cpp @@ -1,3 +1,7 @@ +// Copyright 2025, The FileReadSpeedTest Contributors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + #include "GetProcessorCoreInformation.hpp" #ifndef WIN32_LEAN_AND_MEAN diff --git a/Code/GetProcessorCoreInformation.hpp b/Code/GetProcessorCoreInformation.hpp index 5940789..f32b86f 100644 --- a/Code/GetProcessorCoreInformation.hpp +++ b/Code/GetProcessorCoreInformation.hpp @@ -1,3 +1,7 @@ +// Copyright 2025, The FileReadSpeedTest Contributors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + #ifndef GETPROCESSORCOREINFORMATION_HPP #define GETPROCESSORCOREINFORMATION_HPP