Last active
February 10, 2026 07:07
-
-
Save samueljon/e7818edeb218f5e2f1e3e258949d04c8 to your computer and use it in GitHub Desktop.
Disable / Enable HyperThreading cores on runtime - linux
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| #!/bin/bash | |
| HYPERTHREADING=1 | |
| TABLE_VIEW=0 # Default to list view | |
| AUTO_MODE=0 # Automatic enable/disable without prompt | |
| # Color codes | |
| BOLD='\033[1m' | |
| GREEN='\033[0;32m' | |
| RED='\033[0;31m' | |
| ORANGE='\033[0;33m' | |
| RESET='\033[0m' | |
| # Global arrays to cache topology information | |
| declare -A CACHED_SIBLINGS # Maps thread ID to its sibling list | |
| declare -A CACHED_SIBLING_MAP # Maps each thread to its sibling | |
| declare -A CACHED_PRIMARY # Maps each thread to 1 if primary, 0 if HT sibling | |
| # Parse command line arguments | |
| while [[ $# -gt 0 ]]; do | |
| case $1 in | |
| -t|--table) | |
| TABLE_VIEW=1 | |
| shift | |
| ;; | |
| -e|--enable) | |
| AUTO_MODE=1 | |
| HYPERTHREADING=1 | |
| shift | |
| ;; | |
| -d|--disable) | |
| AUTO_MODE=1 | |
| HYPERTHREADING=0 | |
| shift | |
| ;; | |
| -h|--help) | |
| echo "Usage: $0 [OPTIONS]" | |
| echo "" | |
| echo "Options:" | |
| echo " -e, --enable Enable hyperthreading and exit" | |
| echo " -d, --disable Disable hyperthreading and exit" | |
| echo " -t, --table Display threads in table format grouped by physical cores" | |
| echo " -h, --help Show this help message" | |
| echo "" | |
| echo "Examples:" | |
| echo " $0 # Interactive mode with list view" | |
| echo " $0 -t # Interactive mode with table view" | |
| echo " $0 -d # Disable HT immediately" | |
| echo " $0 -e -t # Enable HT and show table view" | |
| exit 0 | |
| ;; | |
| *) | |
| echo "Unknown option: $1" | |
| echo "Use -h or --help for usage information" | |
| exit 1 | |
| ;; | |
| esac | |
| done | |
| function cacheTopology() { | |
| # Cache topology information - temporarily enable offline threads to read topology | |
| # Save original state | |
| declare -A original_state | |
| for CPU in $(printf '%s\n' /sys/devices/system/cpu/cpu[0-9]* | sort -V); do | |
| CPUID=`basename $CPU | cut -b4-` | |
| # Save original state | |
| if [ -e $CPU/online ]; then | |
| original_state[$CPUID]=$(cat $CPU/online 2>/dev/null || echo "1") | |
| # Temporarily bring thread online to read topology | |
| [ -w $CPU/online ] && echo "1" > $CPU/online 2>/dev/null | |
| else | |
| original_state[$CPUID]=1 # Thread 0 is always online | |
| fi | |
| done | |
| # Small delay to ensure threads are fully online | |
| sleep 0.1 | |
| # Read topology from all threads | |
| for CPU in $(printf '%s\n' /sys/devices/system/cpu/cpu[0-9]* | sort -V); do | |
| CPUID=`basename $CPU | cut -b4-` | |
| # Cache sibling list if available | |
| if [ -e $CPU/topology/thread_siblings_list ]; then | |
| CACHED_SIBLINGS[$CPUID]=`cat $CPU/topology/thread_siblings_list` | |
| # Parse and build sibling map | |
| SIBLINGS="${CACHED_SIBLINGS[$CPUID]}" | |
| PRIMARY=$(echo "$SIBLINGS" | cut -d',' -f1 | cut -d'-' -f1) | |
| SECONDARY=$(echo "$SIBLINGS" | cut -d',' -f2 | cut -d'-' -f2) | |
| if [ "$PRIMARY" != "$SECONDARY" ] && [ -n "$SECONDARY" ]; then | |
| # Map both directions | |
| CACHED_SIBLING_MAP[$PRIMARY]=$SECONDARY | |
| CACHED_SIBLING_MAP[$SECONDARY]=$PRIMARY | |
| # Mark which is primary (first in siblings list) | |
| CACHED_PRIMARY[$PRIMARY]=1 | |
| CACHED_PRIMARY[$SECONDARY]=0 | |
| fi | |
| fi | |
| done | |
| # Restore original state | |
| for CPU in $(printf '%s\n' /sys/devices/system/cpu/cpu[0-9]* | sort -V); do | |
| CPUID=`basename $CPU | cut -b4-` | |
| ORIG_STATE="${original_state[$CPUID]}" | |
| if [ -n "$ORIG_STATE" ] && [ -w $CPU/online ]; then | |
| echo "$ORIG_STATE" > $CPU/online 2>/dev/null | |
| fi | |
| done | |
| } | |
| function toggleHyperThreading() { | |
| # Only show line-by-line output if not in table view | |
| local SHOW_OUTPUT=1 | |
| if [ "$TABLE_VIEW" -eq "1" ]; then | |
| SHOW_OUTPUT=0 | |
| fi | |
| for CPU in $(printf '%s\n' /sys/devices/system/cpu/cpu[0-9]* | sort -V); do | |
| CPUID=`basename $CPU | cut -b4-` | |
| [ "$SHOW_OUTPUT" -eq "1" ] && echo -en "${BOLD}Thread:${RESET} $CPUID\t" | |
| [ -w $CPU/online ] && echo "1" > $CPU/online | |
| THREAD1=`cat $CPU/topology/thread_siblings_list | cut -f1 -d,` | |
| if [ $CPUID = $THREAD1 ]; then | |
| [ "$SHOW_OUTPUT" -eq "1" ] && echo -e "${GREEN}-> online (primary)${RESET}" | |
| [ -w $CPU/online ] && echo "1" > $CPU/online | |
| else | |
| if [ "$HYPERTHREADING" -eq "0" ]; then | |
| [ "$SHOW_OUTPUT" -eq "1" ] && echo -e "${RED}-> offline (HT sibling)${RESET}" | |
| else | |
| [ "$SHOW_OUTPUT" -eq "1" ] && echo -e "${ORANGE}-> online (HT sibling)${RESET}" | |
| fi | |
| [ -w $CPU/online ] && echo "$HYPERTHREADING" > $CPU/online | |
| fi | |
| done | |
| } | |
| function enabled() { | |
| echo -e "${GREEN}${BOLD}Enabling Hyperthreading (bringing HT sibling threads online)${RESET}\n" | |
| HYPERTHREADING=1 | |
| toggleHyperThreading | |
| # Show result in appropriate format | |
| if [ "$TABLE_VIEW" -eq "1" ]; then | |
| echo "" | |
| showCurrentStateTable | |
| fi | |
| } | |
| function disabled() { | |
| echo -e "${RED}${BOLD}Disabling Hyperthreading (taking HT sibling threads offline)${RESET}\n" | |
| HYPERTHREADING=0 | |
| toggleHyperThreading | |
| # Show result in appropriate format | |
| if [ "$TABLE_VIEW" -eq "1" ]; then | |
| echo "" | |
| showCurrentStateTable | |
| fi | |
| } | |
| function showCurrentState() { | |
| echo -e "${BOLD}Current Thread Status:${RESET}\n" | |
| for CPU in $(printf '%s\n' /sys/devices/system/cpu/cpu[0-9]* | sort -V); do | |
| CPUID=`basename $CPU | cut -b4-` | |
| echo -en "${BOLD}Thread:${RESET} $CPUID\t" | |
| # Check if thread is online | |
| if [ -e $CPU/online ]; then | |
| ONLINE_STATUS=$(cat $CPU/online) | |
| else | |
| # Thread 0 doesn't have online file, it's always online | |
| ONLINE_STATUS=1 | |
| fi | |
| # Check if thread is offline - if so, topology files won't exist | |
| if [ "$ONLINE_STATUS" -eq "0" ]; then | |
| echo -e "${RED}-> offline (HT sibling)${RESET}" | |
| continue | |
| fi | |
| # For online threads, determine if it's a primary or secondary thread | |
| if [ -e $CPU/topology/thread_siblings_list ]; then | |
| THREAD1=`cat $CPU/topology/thread_siblings_list | cut -f1 -d,` | |
| if [ $CPUID = $THREAD1 ]; then | |
| # Primary thread | |
| echo -e "${GREEN}-> online (primary)${RESET}" | |
| else | |
| # Secondary thread (HT sibling) | |
| echo -e "${ORANGE}-> online (HT sibling)${RESET}" | |
| fi | |
| else | |
| # Topology file doesn't exist, just show online status | |
| echo -e "${GREEN}-> online${RESET}" | |
| fi | |
| done | |
| echo "" | |
| } | |
| function showCurrentStateTable() { | |
| # Get current status for all threads (topology is cached globally) | |
| declare -A cpu_status | |
| declare -A processed | |
| # Get current online/offline status for all threads | |
| for CPU in $(printf '%s\n' /sys/devices/system/cpu/cpu[0-9]* | sort -V); do | |
| CPUID=`basename $CPU | cut -b4-` | |
| # Get online status | |
| if [ -e $CPU/online ]; then | |
| cpu_status[$CPUID]=$(cat $CPU/online) | |
| else | |
| cpu_status[$CPUID]=1 # Thread 0 is always online | |
| fi | |
| done | |
| # Print table header | |
| echo -e "${BOLD}┌───────────────────────┬───────────────────────┐${RESET}" | |
| echo -e "${BOLD}│ Primary Thread │ HT Sibling │${RESET}" | |
| echo -e "${BOLD}├───────────────────────┼───────────────────────┤${RESET}" | |
| # Display all threads in pairs using cached topology | |
| for CPU in $(printf '%s\n' /sys/devices/system/cpu/cpu[0-9]* | sort -V); do | |
| CPUID=`basename $CPU | cut -b4-` | |
| # Skip if already processed | |
| [ "${processed[$CPUID]}" = "1" ] && continue | |
| # Check if this thread has a sibling (use cached data) | |
| SIBLING="${CACHED_SIBLING_MAP[$CPUID]}" | |
| if [ -n "$SIBLING" ]; then | |
| # Mark both as processed | |
| processed[$CPUID]=1 | |
| processed[$SIBLING]=1 | |
| # Get status for both threads | |
| STATUS1="${cpu_status[$CPUID]}" | |
| STATUS2="${cpu_status[$SIBLING]}" | |
| # Determine display order: online thread goes in Primary column | |
| # If both online or both offline, use cached primary designation | |
| if [ "$STATUS1" -eq "1" ] && [ "$STATUS2" -eq "0" ]; then | |
| # CPUID is online, SIBLING is offline - CPUID is primary | |
| PRIMARY=$CPUID | |
| SECONDARY=$SIBLING | |
| elif [ "$STATUS1" -eq "0" ] && [ "$STATUS2" -eq "1" ]; then | |
| # SIBLING is online, CPUID is offline - SIBLING is primary | |
| PRIMARY=$SIBLING | |
| SECONDARY=$CPUID | |
| else | |
| # Both same status - use cached topology | |
| if [ "${CACHED_PRIMARY[$CPUID]}" = "1" ]; then | |
| PRIMARY=$CPUID | |
| SECONDARY=$SIBLING | |
| else | |
| PRIMARY=$SIBLING | |
| SECONDARY=$CPUID | |
| fi | |
| fi | |
| # Get status for primary (should be online or both same status) | |
| PRIMARY_STATUS="${cpu_status[$PRIMARY]}" | |
| if [ "$PRIMARY_STATUS" -eq "1" ]; then | |
| PRIMARY_COLOR="${GREEN}" | |
| PRIMARY_SYMBOL="✓" | |
| else | |
| PRIMARY_COLOR="${RED}" | |
| PRIMARY_SYMBOL="✗" | |
| fi | |
| # Build display strings | |
| PRIMARY_TEXT=$(printf "Thread %-2s %s" "$PRIMARY" "$PRIMARY_SYMBOL") | |
| # Get status for secondary (HT sibling) | |
| SECONDARY_STATUS="${cpu_status[$SECONDARY]}" | |
| if [ "$SECONDARY_STATUS" -eq "1" ]; then | |
| HT_COLOR="${ORANGE}" | |
| HT_SYMBOL="✓" | |
| else | |
| HT_COLOR="${RED}" | |
| HT_SYMBOL="✗" | |
| fi | |
| HT_TEXT=$(printf "Thread %-2s %s" "$SECONDARY" "$HT_SYMBOL") | |
| # Print row with proper alignment | |
| printf "│ ${PRIMARY_COLOR}%-16s${RESET} \t│ ${HT_COLOR}%-16s${RESET} \t│\n" \ | |
| "$PRIMARY_TEXT" "$HT_TEXT" | |
| else | |
| # Single-threaded core (no HT sibling) | |
| processed[$CPUID]=1 | |
| STATUS="${cpu_status[$CPUID]}" | |
| if [ "$STATUS" -eq "1" ]; then | |
| COLOR="${GREEN}" | |
| SYMBOL="✓" | |
| else | |
| COLOR="${RED}" | |
| SYMBOL="✗" | |
| fi | |
| THREAD_TEXT=$(printf "Thread %-2s %s" "$CPUID" "$SYMBOL") | |
| printf "│ ${COLOR}%-16s${RESET} \t│ %-16s \t│\n" \ | |
| "$THREAD_TEXT" "N/A" | |
| fi | |
| done | |
| echo -e "${BOLD}└───────────────────────┴───────────────────────┘${RESET}" | |
| echo "" | |
| echo -e "Legend: ${GREEN}✓${RESET} = Online (Primary) ${ORANGE}✓${RESET} = Online (HT) ${RED}✗${RESET} = Offline" | |
| echo "" | |
| } | |
| # | |
| ONLINE=$(cat /sys/devices/system/cpu/online) | |
| OFFLINE=$(cat /sys/devices/system/cpu/offline) | |
| if [[ $EUID -ne 0 ]]; then | |
| echo "This script must be run as root" | |
| exit 1 | |
| fi | |
| # Cache topology information while all threads are online | |
| cacheTopology | |
| # If auto mode is enabled, toggle and exit | |
| if [ "$AUTO_MODE" -eq "1" ]; then | |
| if [ "$HYPERTHREADING" -eq "1" ]; then | |
| enabled | |
| else | |
| disabled | |
| fi | |
| # For list view, show final summary | |
| if [ "$TABLE_VIEW" -eq "0" ]; then | |
| echo "" | |
| echo "---------------------------------------------------" | |
| ONLINE=$(cat /sys/devices/system/cpu/online) | |
| OFFLINE=$(cat /sys/devices/system/cpu/offline) | |
| echo -en "Threads online: $ONLINE\t Threads offline: $OFFLINE\n" | |
| echo "---------------------------------------------------" | |
| fi | |
| exit 0 | |
| fi | |
| # Interactive mode - show current state first | |
| if [ "$TABLE_VIEW" -eq "1" ]; then | |
| showCurrentStateTable | |
| else | |
| echo "---------------------------------------------------" | |
| echo -en "Threads online: $ONLINE\t Threads offline: $OFFLINE\n" | |
| echo "---------------------------------------------------" | |
| showCurrentState | |
| echo "---------------------------------------------------" | |
| fi | |
| # Interactive prompt | |
| while true; do | |
| read -p "Type in e to enable or d to disable hyperthreading or q to quit [e/d/q] ?" ed | |
| case $ed in | |
| [Ee]* ) enabled; break;; | |
| [Dd]* ) disabled;exit;; | |
| [Qq]* ) exit;; | |
| * ) echo "Please answer e for enable or d for disable hyperthreading.";; | |
| esac | |
| done |
Author
Thanks for reporting this @antermin and I have mades some improvements and also taken the considerations from @rorar into account. Here is the changelog:
Display Enhancements
- Add table view mode (-t/--table flag) showing thread pairs grouped by physical core
- Use Unicode box-drawing characters for clean table borders
- Add color-coded output:
- Green: online primary threads
- Orange: online HT sibling threads
- Red: offline threads
- Bold formatting for labels and headers
Bug Fixes
- Fix CPU ordering to use numeric sort instead of lexicographic (0,1,2...10,11 not 0,1,10,11)
- Handle CPU 0 permission errors (boot CPU cannot be taken offline)
- Check file writability before attempting to modify /sys/devices/system/cpu/*/online
Functionality Improvements
- Add non-interactive mode with -e/--enable and -d/--disable flags
- Cache topology information at startup to handle offline threads correctly
- Smart display logic: online threads are always shown in the Primary column when HT is disabled
- Preserve thread pairing information even when siblings are offline
- Temporarily enable offline threads during cache to read topology files
Terminology Updates
- Use "Thread" instead of "CPU" for logical processors
- Use "Physical Core" for hardware cores
- Clarify "Primary Thread" vs "HT Sibling" throughout
- Update all messages and labels for consistency
Architecture
- Global topology cache (CACHED_SIBLINGS, CACHED_SIBLING_MAP, CACHED_PRIMARY)
- Separate functions for list and table views
- cacheTopology() function to preserve sibling relationships
- Conditional output in toggleHyperThreading() based on view mode
Usage
- Interactive mode: ./toggleHT.sh [-t]
- Direct disable: ./toggleHT.sh -d [-t]
- Direct enable: ./toggleHT.sh -e [-t]
- Help: ./toggleHT.sh -h
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Hello, I noticed that the script does not output the CPUs in correct order.
Actual output:
Expected output: