This commit is contained in:
Jose Drowne 2025-12-22 21:49:31 +00:00 committed by GitHub
commit 7959c5ec6b
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
3 changed files with 197 additions and 1 deletions

View File

@ -79,6 +79,7 @@
#endif
#include "verilated_threads.h"
#include "verilated_threading_advisor.h"
// clang-format on
#include "verilated_trace.h"
@ -2881,7 +2882,14 @@ void VerilatedContext::addModel(const VerilatedModel* modelp) {
VerilatedVirtualBase* VerilatedContext::threadPoolp() {
if (m_threads == 1) return nullptr;
if (!m_threadPool) m_threadPool.reset(new VlThreadPool{this, m_threads - 1});
if (!m_threadPool) {
m_threadPool.reset(new VlThreadPool{this, m_threads - 1});
// Run threading advisor if enabled via +verilator+threading+advisor
if (threadingAdvisor()) {
static VlThreadingAdvisor advisor;
advisor.analyze(m_threads, quiet());
}
}
return m_threadPool.get();
}
@ -2998,6 +3006,8 @@ void VerilatedContextImp::commandArgVl(const std::string& arg) {
profExecFilename(str);
} else if (commandArgVlString(arg, "+verilator+prof+vlt+file+", str)) {
profVltFilename(str);
} else if (arg == "+verilator+threading+advisor") {
threadingAdvisor(true);
} else if (arg == "+verilator+quiet") {
quiet(true);
} else if (commandArgVlUint64(arg, "+verilator+rand+reset+", u64, 0, 2)) {

View File

@ -406,6 +406,7 @@ protected:
// Fast path
uint64_t m_profExecStart = 1; // +prof+exec+start time
uint32_t m_profExecWindow = 2; // +prof+exec+window size
bool m_threadingAdvisor = false; // +threading+advisor enabled
// Slow path
std::string m_coverageFilename; // +coverage+file filename
std::string m_profExecFilename; // +prof+exec+file filename
@ -647,6 +648,10 @@ public:
std::string profVltFilename() const VL_MT_SAFE;
void profVltFilename(const std::string& flag) VL_MT_SAFE;
// Internal: Threading advisor
bool threadingAdvisor() const VL_MT_SAFE { return m_ns.m_threadingAdvisor; }
void threadingAdvisor(bool flag) VL_MT_SAFE { m_ns.m_threadingAdvisor = flag; }
// Internal: SMT solver program
std::string solverProgram() const VL_MT_SAFE;
void solverProgram(const std::string& flag) VL_MT_SAFE;

View File

@ -0,0 +1,181 @@
// -*- mode: C++; c-file-style: "cc-mode" -*-
//=============================================================================
//
// Code available from: https://verilator.org
//
// Copyright 2025 by Wilson Snyder. This program is free software; you
// can redistribute it and/or modify it under the terms of either the GNU
// Lesser General Public License Version 3 or the Perl Artistic License
// Version 2.0.
// SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0
//
//=============================================================================
///
/// \file
/// \brief Verilated runtime threading advisor
///
/// This file provides runtime analysis of threading configuration and
/// emits warnings when potential issues are detected. Unlike verilator_gantt
/// which requires explicit profiling and post-hoc analysis, this advisor
/// checks for common configuration issues automatically.
///
/// This file is not part of the Verilated public-facing API.
/// It is only for internal use by Verilated library routines.
///
//=============================================================================
#ifndef VERILATOR_VERILATED_THREADING_ADVISOR_H_
#define VERILATOR_VERILATED_THREADING_ADVISOR_H_
#include "verilatedos.h"
#include <cstdint>
#include <cstdio>
#include <fstream>
#include <set>
#include <sstream>
#include <string>
#include <utility>
#include <vector>
//=============================================================================
// VlThreadingAdvisor - Runtime threading configuration advisor
//
// Analyzes CPU topology and threading configuration to detect potential
// performance issues. This is advisory only - it does not affect simulation.
class VlThreadingAdvisor final {
// TYPES
struct CpuInfo final {
int cpuId = -1; // Logical CPU ID
int physicalId = -1; // Physical socket/package ID
int coreId = -1; // Core ID within socket
bool valid = false; // True if info was successfully read
};
// MEMBERS
std::vector<CpuInfo> m_cpuInfo; // CPU topology information
unsigned m_physicalCores = 0; // Number of physical cores
unsigned m_logicalCpus = 0; // Number of logical CPUs (hw threads)
unsigned m_sockets = 0; // Number of CPU sockets
bool m_hyperthreading = false; // True if SMT/hyperthreading detected
bool m_topologyRead = false; // True if topology was successfully read
public:
// CONSTRUCTORS
VlThreadingAdvisor() { readTopology(); }
~VlThreadingAdvisor() = default;
// METHODS
/// Analyze threading configuration and emit warnings if issues detected
/// @param nthreads Number of simulation threads (including main thread)
/// @param quiet If true, suppress all output
void analyze(unsigned nthreads, bool quiet) const {
if (quiet) return;
if (nthreads <= 1) return; // Single-threaded, nothing to check
if (!m_topologyRead) return; // Can't analyze without topology
checkThreadCount(nthreads);
checkHyperthreading(nthreads);
}
// ACCESSORS
unsigned physicalCores() const { return m_physicalCores; }
unsigned logicalCpus() const { return m_logicalCpus; }
unsigned sockets() const { return m_sockets; }
bool hasHyperthreading() const { return m_hyperthreading; }
private:
void readTopology() {
#if defined(__linux__)
readTopologyLinux();
#elif defined(__APPLE__)
readTopologyMacOS();
#endif
}
#if defined(__linux__)
void readTopologyLinux() {
// Read CPU topology from /sys/devices/system/cpu/
const unsigned maxCpus = VlOs::getProcessDefaultParallelism();
m_cpuInfo.resize(maxCpus);
std::set<int> sockets;
std::set<std::pair<int, int>> physCores; // (socket, core) pairs
for (unsigned cpu = 0; cpu < maxCpus; ++cpu) {
CpuInfo& info = m_cpuInfo[cpu];
info.cpuId = static_cast<int>(cpu);
// Read physical package ID (socket)
std::ostringstream physPath;
physPath << "/sys/devices/system/cpu/cpu" << cpu
<< "/topology/physical_package_id";
std::ifstream physFile(physPath.str());
if (physFile.good()) {
physFile >> info.physicalId;
sockets.insert(info.physicalId);
}
// Read core ID
std::ostringstream corePath;
corePath << "/sys/devices/system/cpu/cpu" << cpu << "/topology/core_id";
std::ifstream coreFile(corePath.str());
if (coreFile.good()) {
coreFile >> info.coreId;
if (info.physicalId >= 0) {
physCores.insert({info.physicalId, info.coreId});
}
}
info.valid = (info.physicalId >= 0 && info.coreId >= 0);
}
m_logicalCpus = maxCpus;
m_sockets = static_cast<unsigned>(sockets.size());
m_physicalCores = static_cast<unsigned>(physCores.size());
m_hyperthreading = (m_logicalCpus > m_physicalCores);
m_topologyRead = (m_physicalCores > 0);
}
#endif
#if defined(__APPLE__)
void readTopologyMacOS() {
// On macOS, use sysctl to get CPU info
// For simplicity, just use basic heuristics
m_logicalCpus = VlOs::getProcessDefaultParallelism();
// Apple Silicon doesn't have traditional hyperthreading
// Intel Macs may have it - assume no HT for simplicity
m_physicalCores = m_logicalCpus;
m_sockets = 1;
m_hyperthreading = false;
m_topologyRead = true;
}
#endif
void checkThreadCount(unsigned nthreads) const {
// Check if requesting more threads than physical cores
if (m_hyperthreading && nthreads > m_physicalCores) {
std::fprintf(stderr,
"%%Warning: Simulation uses %u threads but system has only %u physical "
"cores (%u logical CPUs with hyperthreading).\n",
nthreads, m_physicalCores, m_logicalCpus);
std::fprintf(stderr,
" : Using more threads than physical cores often reduces "
"performance.\n");
std::fprintf(stderr,
" : Consider --threads %u or use numactl to bind to physical "
"cores.\n",
m_physicalCores);
}
}
void checkHyperthreading(unsigned /*nthreads*/) const {
// On systems with hyperthreading, could warn about potential core sharing
// However, this is too noisy for default behavior. Users who want detailed
// analysis should use +verilator+prof+exec and verilator_gantt.
}
};
#endif // VERILATOR_VERILATED_THREADING_ADVISOR_H_