Merge 56fcdc05cb into 742c0b134c
This commit is contained in:
commit
7959c5ec6b
|
|
@ -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)) {
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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_
|
||||
Loading…
Reference in New Issue