diff --git a/.github/workflows/reusable-rtlmeter-build.yml b/.github/workflows/reusable-rtlmeter-build.yml index 345abbee7..85deb9706 100644 --- a/.github/workflows/reusable-rtlmeter-build.yml +++ b/.github/workflows/reusable-rtlmeter-build.yml @@ -36,8 +36,8 @@ jobs: echo "path-exclude /usr/share/info/*" | sudo tee -a /etc/dpkg/dpkg.cfg.d/01_nodoc sudo apt update || \ sudo apt update - sudo apt install ccache mold help2man libfl-dev libgoogle-perftools-dev libsystemc-dev || \ - sudo apt install ccache mold help2man libfl-dev libgoogle-perftools-dev libsystemc-dev + sudo apt install ccache mold help2man libfl-dev libjemalloc-dev libsystemc-dev || \ + sudo apt install ccache mold help2man libfl-dev libjemalloc-dev libsystemc-dev - name: Use saved ccache uses: actions/cache@v5 diff --git a/.github/workflows/reusable-rtlmeter-run.yml b/.github/workflows/reusable-rtlmeter-run.yml index c2746209c..5e163f555 100644 --- a/.github/workflows/reusable-rtlmeter-run.yml +++ b/.github/workflows/reusable-rtlmeter-run.yml @@ -60,8 +60,8 @@ jobs: echo "path-exclude /usr/share/info/*" | sudo tee -a /etc/dpkg/dpkg.cfg.d/01_nodoc sudo apt update || \ sudo apt update - sudo apt install ccache mold libfl-dev libgoogle-perftools-dev libsystemc-dev || \ - sudo apt install ccache mold libfl-dev libgoogle-perftools-dev libsystemc-dev + sudo apt install ccache mold libfl-dev libjemalloc-dev libsystemc-dev || \ + sudo apt install ccache mold libfl-dev libjemalloc-dev libsystemc-dev - name: Download Verilator installation archive uses: actions/download-artifact@v8 diff --git a/ci/ci-install.bash b/ci/ci-install.bash index fe3ed6e4e..91b0f1bfc 100755 --- a/ci/ci-install.bash +++ b/ci/ci-install.bash @@ -60,8 +60,8 @@ if [ "$CI_BUILD_STAGE_NAME" = "build" ]; then sudo apt-get install ccache help2man libfl-dev if [[ ! "$CI_RUNS_ON" =~ "ubuntu-22.04" ]]; then # Some conflict of libunwind verison on 22.04, can live without it for now - sudo apt-get install libgoogle-perftools-dev || - sudo apt-get install libgoogle-perftools-dev + sudo apt-get install libjemalloc-dev || + sudo apt-get install libjemalloc-dev fi if [[ "$CI_RUNS_ON" =~ "ubuntu-20.04" ]] || [[ "$CI_RUNS_ON" =~ "ubuntu-22.04" ]] || [[ "$CI_RUNS_ON" =~ "ubuntu-24.04" ]]; then sudo apt-get install libsystemc libsystemc-dev || diff --git a/ci/docker/buildenv/Dockerfile b/ci/docker/buildenv/Dockerfile index 29a8c1d2d..d205c08e2 100644 --- a/ci/docker/buildenv/Dockerfile +++ b/ci/docker/buildenv/Dockerfile @@ -37,7 +37,7 @@ RUN apt-get update \ libfl2 \ libfl-dev \ libclang-rt-18-dev \ - libgoogle-perftools-dev \ + libjemalloc-dev \ libsystemc \ libsystemc-dev \ numactl \ diff --git a/ci/docker/run/Dockerfile b/ci/docker/run/Dockerfile index 8a6637906..495a83cb3 100644 --- a/ci/docker/run/Dockerfile +++ b/ci/docker/run/Dockerfile @@ -22,7 +22,7 @@ RUN apt-get update \ help2man \ libfl2 \ libfl-dev \ - libgoogle-perftools-dev \ + libjemalloc-dev \ numactl \ perl \ perl-doc \ diff --git a/configure.ac b/configure.ac index 4b1ae8f8d..9b3aa1fdd 100644 --- a/configure.ac +++ b/configure.ac @@ -64,8 +64,8 @@ AC_ARG_ENABLE([dev-asan], [AS_HELP_STRING([--enable-dev-asan], [Enable compiling Verilator with ASAN AddressSanitizer for memory error detection. - This disables tcmalloc. Does not affect - Verilated models using ASAN.])], + This disables tcmalloc and jemalloc. Does not + affect Verilated models using ASAN.])], [case "${enableval}" in yes) CFG_WITH_DEV_ASAN=yes ;; no) CFG_WITH_DEV_ASAN=no ;; @@ -93,6 +93,27 @@ else AC_MSG_RESULT($CFG_WITH_TCMALLOC) fi +# Flag to enable linking Verilator with jemalloc if available +AC_MSG_CHECKING(whether to use jemalloc) +AC_ARG_ENABLE([jemalloc], + [AS_HELP_STRING([--enable-jemalloc], + [Use libjemalloc for faster dynamic memory + management in Verilator binary. Preferred over + tcmalloc when both are available + @<:@default=check@:>@])], + [case "${enableval}" in + yes) CFG_WITH_JEMALLOC=yes ;; + no) CFG_WITH_JEMALLOC=no ;; + *) AC_MSG_ERROR([bad value '${enableval}' for --enable-jemalloc]) ;; + esac], + [CFG_WITH_JEMALLOC=check;]) +if test "$CFG_WITH_DEV_ASAN" = "yes"; then + CFG_WITH_JEMALLOC=no + AC_MSG_RESULT("disabled by --enable-dev-asan") +else + AC_MSG_RESULT($CFG_WITH_JEMALLOC) +fi + # Flag to enable code coverage build with gcov AC_MSG_CHECKING(whether to build for gcov code coverage collection) AC_ARG_ENABLE([dev-gcov], @@ -567,8 +588,10 @@ if test "$CFG_ENABLE_PARTIAL_STATIC" = "yes"; then _MY_LDLIBS_CHECK_OPT(CFG_LDFLAGS_SRC, -static-libstdc++) _MY_LDLIBS_CHECK_OPT(CFG_LDFLAGS_SRC, -Xlinker -gc-sections) LTCMALLOC="-Wl,--whole-archive -l:libtcmalloc_minimal.a -Wl,--no-whole-archive" + LJEMALLOC="-Wl,--whole-archive -l:libjemalloc.a -Wl,--no-whole-archive" else LTCMALLOC=-ltcmalloc_minimal + LJEMALLOC=-ljemalloc fi AC_SUBST(CFG_LDFLAGS_SRC) AC_SUBST(CFG_LDFLAGS_VERILATED) @@ -584,11 +607,38 @@ _MY_LDLIBS_CHECK_OPT(CFG_LIBS, -latomic) _MY_LDLIBS_CHECK_OPT(CFG_LIBS, -lbcrypt) _MY_LDLIBS_CHECK_OPT(CFG_LIBS, -lpsapi) +# Check if jemalloc is available based on --enable-jemalloc +# jemalloc is preferred over tcmalloc when both are available +CFG_HAVE_JEMALLOC=no +_MY_LDLIBS_CHECK_IFELSE( + $LJEMALLOC, + [if test "$CFG_WITH_JEMALLOC" != "no"; then + CFG_LIBS="$LJEMALLOC $CFG_LIBS"; + CFG_HAVE_JEMALLOC=yes; + # If using jemalloc, add some extra options to make the compiler not assume + # it is using its own versions of the standard library functions + _MY_CXX_CHECK_OPT(CFG_CXXFLAGS_SRC,-fno-builtin-malloc) + _MY_CXX_CHECK_OPT(CFG_CXXFLAGS_SRC,-fno-builtin-calloc) + _MY_CXX_CHECK_OPT(CFG_CXXFLAGS_SRC,-fno-builtin-realloc) + _MY_CXX_CHECK_OPT(CFG_CXXFLAGS_SRC,-fno-builtin-free) + AC_DEFINE([HAVE_JEMALLOC],[1],[Defined if have jemalloc]) + fi], + [if test "$CFG_WITH_JEMALLOC" = "yes"; then + AC_MSG_ERROR([--enable-jemalloc was given but test for ${LJEMALLOC} failed]) + fi]) +AC_SUBST(HAVE_JEMALLOC) + # Check if tcmalloc is available based on --enable-tcmalloc +# Only use tcmalloc if jemalloc was not found/enabled +if test "$CFG_HAVE_JEMALLOC" = "yes"; then + AC_MSG_NOTICE([jemalloc found, skipping tcmalloc check]) +else +CFG_HAVE_TCMALLOC=no _MY_LDLIBS_CHECK_IFELSE( $LTCMALLOC, [if test "$CFG_WITH_TCMALLOC" != "no"; then CFG_LIBS="$LTCMALLOC $CFG_LIBS"; + CFG_HAVE_TCMALLOC=yes; # If using tcmalloc, add some extra options to make the compiler not assume # it is using its own versions of the standard library functions _MY_CXX_CHECK_OPT(CFG_CXXFLAGS_SRC,-fno-builtin-malloc) @@ -600,6 +650,7 @@ _MY_LDLIBS_CHECK_IFELSE( [if test "$CFG_WITH_TCMALLOC" = "yes"; then AC_MSG_ERROR([--enable-tcmalloc was given but test for ${LTCMALLOC} failed]) fi]) +fi AC_SUBST(HAVE_TCMALLOC) AC_SUBST(CFG_LIBS) diff --git a/docs/guide/install.rst b/docs/guide/install.rst index 989c581c6..8ebb45110 100644 --- a/docs/guide/install.rst +++ b/docs/guide/install.rst @@ -61,7 +61,7 @@ In brief, to install from git: # Prerequisites: #sudo apt-get install git help2man perl python3 make autoconf g++ flex bison ccache - #sudo apt-get install libgoogle-perftools-dev numactl perl-doc + #sudo apt-get install libgoogle-perftools-dev libjemalloc-dev numactl perl-doc #sudo apt-get install libfl2 # Ubuntu only (ignore if gives error) #sudo apt-get install libfl-dev # Ubuntu only (ignore if gives error) #sudo apt-get install zlibc zlib1g zlib1g-dev # Ubuntu only (ignore if gives error) @@ -144,7 +144,7 @@ installed for good performance: sudo apt-get install ccache # If present at build, needed for run sudo apt-get install mold # If present at build, needed for run - sudo apt-get install libgoogle-perftools-dev numactl + sudo apt-get install libjemalloc-dev numactl To build Verilator you will need to install these packages; these do not need to be present to run Verilator: diff --git a/src/V3Os.cpp b/src/V3Os.cpp index a34a7aec2..f0b8a8764 100644 --- a/src/V3Os.cpp +++ b/src/V3Os.cpp @@ -57,6 +57,9 @@ VL_DEFINE_DEBUG_FUNCTIONS; #ifdef HAVE_TCMALLOC #include #endif +#ifdef HAVE_JEMALLOC +#include +#endif // clang-format off #if defined(_WIN32) || defined(__MINGW32__) @@ -413,6 +416,18 @@ void V3Os::releaseMemory() { #ifdef HAVE_TCMALLOC MallocExtension::instance()->ReleaseFreeMemory(); #endif +#ifdef HAVE_JEMALLOC + // Purge all unused dirty pages across all arenas + unsigned narenas = 0; + size_t sz = sizeof(narenas); + if (mallctl("arenas.narenas", &narenas, &sz, nullptr, 0)) { + return; // Failed to get number of arenas, give up + } + char buf[64]; + // Index equal to narenas represents all arenas + VL_SNPRINTF(buf, sizeof(buf), "arena.%u.purge", narenas); + mallctl(buf, nullptr, nullptr, nullptr, 0); +#endif } //######################################################################