Skip to content
Open
Show file tree
Hide file tree
Changes from 7 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions check/TestHighsParallel.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -294,6 +294,12 @@ static void nestedTask() {
tg.spawn([]() { loseTime(); });
}

TEST_CASE("AvailableConcurrency", "[parallel]") {
unsigned int cores = highs::parallel::available_concurrency();
REQUIRE(cores > 0);
REQUIRE(cores <= std::thread::hardware_concurrency());
}

TEST_CASE("CancelNestedTasks", "[parallel]") {
highs::parallel::initialize_scheduler();

Expand Down
12 changes: 12 additions & 0 deletions highs/lp_data/Highs.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -5112,6 +5112,18 @@ HighsStatus Highs::initializeMultiThreading() {
highs::parallel::initialize_scheduler(this->options_.threads);
this->max_threads_ = highs::parallel::num_threads();
HighsLogOptions& log_options = this->options_.log_options;
unsigned int available_cores = highs::parallel::available_concurrency();
highsLogDev(log_options, HighsLogType::kInfo,
"Scheduler initialized: %d threads (available cores: %d)\n",
static_cast<int>(this->max_threads_),
static_cast<int>(available_cores));
if (this->max_threads_ > static_cast<int>(available_cores))
highsLogDev(
log_options, HighsLogType::kWarning,
"Thread count (%d) exceeds available cores (%d); consider reducing "
"threads or calling Highs::resetGlobalScheduler()\n",
static_cast<int>(this->max_threads_),
static_cast<int>(available_cores));
if (this->options_.threads != 0 &&
this->max_threads_ != this->options_.threads) {
highsLogUser(
Expand Down
6 changes: 5 additions & 1 deletion highs/parallel/HighsParallel.h
Original file line number Diff line number Diff line change
Expand Up @@ -17,12 +17,16 @@ namespace parallel {

using mutex = HighsMutex;

// Like std::thread::hardware_concurrency(), but respects the process
// CPU affinity mask set by the OS (e.g., taskset or start /affinity).
unsigned int available_concurrency();

inline void initialize_scheduler(int numThreads = 0) {
if (numThreads == 0) {
#ifdef HIGHS_NO_DEFAULT_THREADS
numThreads = 1;
#else
numThreads = (std::thread::hardware_concurrency() + 1) / 2;
numThreads = (available_concurrency() + 1) / 2;
#endif
}
HighsTaskExecutor::initialize(numThreads);
Expand Down
35 changes: 35 additions & 0 deletions highs/parallel/HighsTaskExecutor.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,43 @@
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
#include "parallel/HighsTaskExecutor.h"

#include "parallel/HighsParallel.h"
#include "util/HighsHash.h"

#if defined(__linux__)
#include <sched.h>
#elif defined(_WIN32) || defined(_WIN64)
#ifndef WIN32_LEAN_AND_MEAN
#define WIN32_LEAN_AND_MEAN
#endif
#include <windows.h>
#endif

using namespace highs;

// Like std::thread::hardware_concurrency(), but respects the process
// CPU affinity mask set by the OS (e.g., taskset or start /affinity).
unsigned int highs::parallel::available_concurrency() {
#if defined(__linux__)
// Query the set of CPUs this process is allowed to run on
cpu_set_t set;
if (sched_getaffinity(0, sizeof(set), &set) == 0) {
int count = CPU_COUNT(&set);
if (count > 0) return static_cast<unsigned int>(count);
}
#elif defined(_WIN32) || defined(_WIN64)
// Query the process affinity bitmask and count set bits
DWORD_PTR process_mask, system_mask;
if (GetProcessAffinityMask(GetCurrentProcess(), &process_mask,
&system_mask)) {
int count = HighsHashHelpers::popcnt(static_cast<uint64_t>(process_mask));
if (count > 0) return static_cast<unsigned int>(count);
}
#endif
// Fallback when affinity query is unavailable or fails
return std::thread::hardware_concurrency();
}

#ifdef _WIN32
static thread_local HighsSplitDeque* threadLocalWorkerDequePtr{nullptr};
HighsSplitDeque*& HighsTaskExecutor::threadLocalWorkerDeque() {
Expand Down
Loading