mirror of
https://github.com/uutils/coreutils.git
synced 2026-05-06 07:26:38 -04:00
feat(uucore): add shared hardware detection module
Add shared CPU hardware capability detection in uucore to prevent code duplication across utilities. This provides a unified interface for detecting CPU features (AVX512, AVX2, PCLMUL, SSE2, ASIMD) and respecting GLIBC_TUNABLES environment variable. This unblocks PR #9088 (cksum --debug) and PR #9144 (wc --debug) by providing a common implementation that both utilities can use. Features: - CPU feature detection with caching (singleton pattern) - GLIBC_TUNABLES parsing for hwcaps restrictions - Cross-platform support (x86/x86_64, aarch64) - Comprehensive test coverage - Zero-cost abstractions using std::arch Implementation details: - Uses std::arch feature detection (no external deps for detection) - Adds cfg-if dependency for conditional compilation - Feature-gated behind "hardware" feature flag - Android excluded (no CPUID access in sandboxed environment) Related: #9088, #9144
This commit is contained in:
+13
@@ -188,3 +188,16 @@ nofield
|
||||
# * clippy
|
||||
uninlined
|
||||
nonminimal
|
||||
|
||||
# * CPU/hardware features
|
||||
ASIMD
|
||||
asimd
|
||||
hwcaps
|
||||
PCLMUL
|
||||
pclmul
|
||||
PCLMULQDQ
|
||||
pclmulqdq
|
||||
TUNABLES
|
||||
tunables
|
||||
VMULL
|
||||
vmull
|
||||
|
||||
Generated
+44
-144
@@ -8,15 +8,6 @@ version = "2.0.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "320119579fcad9c21884f5c4861d16174d0e06250625266f50fe6898340abefa"
|
||||
|
||||
[[package]]
|
||||
name = "aho-corasick"
|
||||
version = "1.1.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ddd31a130427c27518df266943a5308ed92d4b226cc639f5a8f1002816174301"
|
||||
dependencies = [
|
||||
"memchr",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "android_system_properties"
|
||||
version = "0.1.5"
|
||||
@@ -58,22 +49,22 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "anstyle-query"
|
||||
version = "1.1.4"
|
||||
version = "1.1.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9e231f6134f61b71076a3eab506c379d4f36122f2af15a9ff04415ea4c3339e2"
|
||||
checksum = "40c48f72fd53cd289104fc64099abca73db4166ad86ea0b4341abe65af83dadc"
|
||||
dependencies = [
|
||||
"windows-sys 0.60.2",
|
||||
"windows-sys 0.61.2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "anstyle-wincon"
|
||||
version = "3.0.10"
|
||||
version = "3.0.11"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3e0633414522a32ffaac8ac6cc8f748e090c5717661fddeea04219e2344f5f2a"
|
||||
checksum = "291e6a250ff86cd4a820112fb8898808a366d8f9f58ce16d1f538353ad55747d"
|
||||
dependencies = [
|
||||
"anstyle",
|
||||
"once_cell_polyfill",
|
||||
"windows-sys 0.60.2",
|
||||
"windows-sys 0.61.2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -205,9 +196,9 @@ checksum = "175812e0be2bccb6abe50bb8d566126198344f707e304f45c648fd8f2cc0365e"
|
||||
|
||||
[[package]]
|
||||
name = "cc"
|
||||
version = "1.2.44"
|
||||
version = "1.2.46"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "37521ac7aabe3d13122dc382493e20c9416f299d2ccd5b3a5340a2570cdeb0f3"
|
||||
checksum = "b97463e1064cb1b1c1384ad0a0b9c8abd0988e2a91f52606c80ef14aadb63e36"
|
||||
dependencies = [
|
||||
"find-msvc-tools",
|
||||
"jobserver",
|
||||
@@ -349,15 +340,14 @@ checksum = "19d374276b40fb8bbdee95aef7c7fa6b5316ec764510eb64b8dd0e2ed0d7e7f5"
|
||||
|
||||
[[package]]
|
||||
name = "crc-fast"
|
||||
version = "1.6.0"
|
||||
version = "1.8.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6ddc2d09feefeee8bd78101665bd8645637828fa9317f9f292496dbbd8c65ff3"
|
||||
checksum = "a2f7c8d397a6353ef0c1d6217ab91b3ddb5431daf57fd013f506b967dcf44458"
|
||||
dependencies = [
|
||||
"crc",
|
||||
"digest",
|
||||
"rand",
|
||||
"regex",
|
||||
"rustversion",
|
||||
"spin",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -402,9 +392,9 @@ checksum = "460fbee9c2c2f33933d720630a6a0bac33ba7053db5344fac858d4b8952d77d5"
|
||||
|
||||
[[package]]
|
||||
name = "crypto-common"
|
||||
version = "0.1.6"
|
||||
version = "0.1.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3"
|
||||
checksum = "78c8292055d1c1df0cce5d180393dc8cce0abec0a7102adb6c7b1eef6016d60a"
|
||||
dependencies = [
|
||||
"generic-array",
|
||||
"typenum",
|
||||
@@ -525,9 +515,9 @@ checksum = "37909eebbb50d72f9059c3b6d82c0463f2ff062c9e95845c43a6c9c0355411be"
|
||||
|
||||
[[package]]
|
||||
name = "find-msvc-tools"
|
||||
version = "0.1.4"
|
||||
version = "0.1.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "52051878f80a721bb68ebfbc930e07b65ba72f2da88968ea5c06fd6ca3d3a127"
|
||||
checksum = "3a3076410a55c90011c298b04d0cfa770b00fa04e1e3c97d3f6c9de105a03844"
|
||||
|
||||
[[package]]
|
||||
name = "flate2"
|
||||
@@ -592,9 +582,9 @@ checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1"
|
||||
|
||||
[[package]]
|
||||
name = "generic-array"
|
||||
version = "0.14.9"
|
||||
version = "0.14.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4bb6743198531e02858aeaea5398fcc883e71851fcbcb5a2f773e2fb6cb1edf2"
|
||||
checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a"
|
||||
dependencies = [
|
||||
"typenum",
|
||||
"version_check",
|
||||
@@ -834,24 +824,24 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "jiff"
|
||||
version = "0.2.15"
|
||||
version = "0.2.16"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "be1f93b8b1eb69c77f24bbb0afdf66f54b632ee39af40ca21c4365a1d7347e49"
|
||||
checksum = "49cce2b81f2098e7e3efc35bc2e0a6b7abec9d34128283d7a26fa8f32a6dbb35"
|
||||
dependencies = [
|
||||
"jiff-static",
|
||||
"jiff-tzdb-platform",
|
||||
"log",
|
||||
"portable-atomic",
|
||||
"portable-atomic-util",
|
||||
"serde",
|
||||
"windows-sys 0.59.0",
|
||||
"serde_core",
|
||||
"windows-sys 0.61.2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "jiff-static"
|
||||
version = "0.2.15"
|
||||
version = "0.2.16"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "03343451ff899767262ec32146f6d559dd759fdadf42ff0e227c7c48f72594b4"
|
||||
checksum = "980af8b43c3ad5d8d349ace167ec8170839f753a42d233ba19e08afe1850fa69"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
@@ -1093,9 +1083,9 @@ checksum = "1a80800c0488c3a21695ea981a54918fbb37abf04f4d0720c453632255e2ff0e"
|
||||
|
||||
[[package]]
|
||||
name = "parse_datetime"
|
||||
version = "0.13.1"
|
||||
version = "0.13.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "77d45119ed61100f40b2389d8ed12e51ec869046d4279afbb5a7c73a4733be36"
|
||||
checksum = "e4955561bc7aa4c40afcfd2a8c34297b13164ae9ac3b30ac348737befdc98e4c"
|
||||
dependencies = [
|
||||
"jiff",
|
||||
"num-traits",
|
||||
@@ -1197,9 +1187,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "quote"
|
||||
version = "1.0.41"
|
||||
version = "1.0.42"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ce25767e7b499d1b604768e7cde645d14cc8584231ea6b295e9c9eb22c02e1d1"
|
||||
checksum = "a338cc41d27e6cc6dce6cefc13a0729dfbb81c262b1f519331575dd80ef3067f"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
]
|
||||
@@ -1259,34 +1249,11 @@ dependencies = [
|
||||
"crossbeam-utils",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "regex"
|
||||
version = "1.12.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "843bc0191f75f3e22651ae5f1e72939ab2f72a4bc30fa80a066bd66edefc24d4"
|
||||
dependencies = [
|
||||
"aho-corasick",
|
||||
"memchr",
|
||||
"regex-automata",
|
||||
"regex-syntax",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "regex-automata"
|
||||
version = "0.4.13"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5276caf25ac86c8d810222b3dbb938e512c55c6831a10f3e6ed1c93b84041f1c"
|
||||
dependencies = [
|
||||
"aho-corasick",
|
||||
"memchr",
|
||||
"regex-syntax",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "regex-syntax"
|
||||
version = "0.8.8"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7a2d987857b319362043e95f5353c0535c1f58eec5336fdfcf626430af7def58"
|
||||
|
||||
[[package]]
|
||||
name = "rust-ini"
|
||||
@@ -1430,6 +1397,12 @@ version = "1.15.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "67b1b7a3b5fe4f1376887184045fcf45c69e92af734b7aaddc05fb777b6fbd03"
|
||||
|
||||
[[package]]
|
||||
name = "spin"
|
||||
version = "0.10.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d5fe4ccb98d9c292d56fec89a5e07da7fc4cf0dc11e156b41793132775d3e591"
|
||||
|
||||
[[package]]
|
||||
name = "stable_deref_trait"
|
||||
version = "1.2.1"
|
||||
@@ -1444,9 +1417,9 @@ checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f"
|
||||
|
||||
[[package]]
|
||||
name = "syn"
|
||||
version = "2.0.108"
|
||||
version = "2.0.110"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "da58917d35242480a05c2897064da0a80589a2a0476c9a3f2fdc83b53502e917"
|
||||
checksum = "a99801b5bd34ede4cf3fc688c5919368fea4e4814a4664359503e6015b280aea"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
@@ -1993,22 +1966,13 @@ dependencies = [
|
||||
"windows-link",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows-sys"
|
||||
version = "0.59.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b"
|
||||
dependencies = [
|
||||
"windows-targets 0.52.6",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows-sys"
|
||||
version = "0.60.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f2f500e4d28234f72040990ec9d39e3a6b950f9f22d3dba18416c35882612bcb"
|
||||
dependencies = [
|
||||
"windows-targets 0.53.5",
|
||||
"windows-targets",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -2020,22 +1984,6 @@ dependencies = [
|
||||
"windows-link",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows-targets"
|
||||
version = "0.52.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973"
|
||||
dependencies = [
|
||||
"windows_aarch64_gnullvm 0.52.6",
|
||||
"windows_aarch64_msvc 0.52.6",
|
||||
"windows_i686_gnu 0.52.6",
|
||||
"windows_i686_gnullvm 0.52.6",
|
||||
"windows_i686_msvc 0.52.6",
|
||||
"windows_x86_64_gnu 0.52.6",
|
||||
"windows_x86_64_gnullvm 0.52.6",
|
||||
"windows_x86_64_msvc 0.52.6",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows-targets"
|
||||
version = "0.53.5"
|
||||
@@ -2043,106 +1991,58 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4945f9f551b88e0d65f3db0bc25c33b8acea4d9e41163edf90dcd0b19f9069f3"
|
||||
dependencies = [
|
||||
"windows-link",
|
||||
"windows_aarch64_gnullvm 0.53.1",
|
||||
"windows_aarch64_msvc 0.53.1",
|
||||
"windows_i686_gnu 0.53.1",
|
||||
"windows_i686_gnullvm 0.53.1",
|
||||
"windows_i686_msvc 0.53.1",
|
||||
"windows_x86_64_gnu 0.53.1",
|
||||
"windows_x86_64_gnullvm 0.53.1",
|
||||
"windows_x86_64_msvc 0.53.1",
|
||||
"windows_aarch64_gnullvm",
|
||||
"windows_aarch64_msvc",
|
||||
"windows_i686_gnu",
|
||||
"windows_i686_gnullvm",
|
||||
"windows_i686_msvc",
|
||||
"windows_x86_64_gnu",
|
||||
"windows_x86_64_gnullvm",
|
||||
"windows_x86_64_msvc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows_aarch64_gnullvm"
|
||||
version = "0.52.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3"
|
||||
|
||||
[[package]]
|
||||
name = "windows_aarch64_gnullvm"
|
||||
version = "0.53.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a9d8416fa8b42f5c947f8482c43e7d89e73a173cead56d044f6a56104a6d1b53"
|
||||
|
||||
[[package]]
|
||||
name = "windows_aarch64_msvc"
|
||||
version = "0.52.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469"
|
||||
|
||||
[[package]]
|
||||
name = "windows_aarch64_msvc"
|
||||
version = "0.53.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b9d782e804c2f632e395708e99a94275910eb9100b2114651e04744e9b125006"
|
||||
|
||||
[[package]]
|
||||
name = "windows_i686_gnu"
|
||||
version = "0.52.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b"
|
||||
|
||||
[[package]]
|
||||
name = "windows_i686_gnu"
|
||||
version = "0.53.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "960e6da069d81e09becb0ca57a65220ddff016ff2d6af6a223cf372a506593a3"
|
||||
|
||||
[[package]]
|
||||
name = "windows_i686_gnullvm"
|
||||
version = "0.52.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66"
|
||||
|
||||
[[package]]
|
||||
name = "windows_i686_gnullvm"
|
||||
version = "0.53.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "fa7359d10048f68ab8b09fa71c3daccfb0e9b559aed648a8f95469c27057180c"
|
||||
|
||||
[[package]]
|
||||
name = "windows_i686_msvc"
|
||||
version = "0.52.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66"
|
||||
|
||||
[[package]]
|
||||
name = "windows_i686_msvc"
|
||||
version = "0.53.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1e7ac75179f18232fe9c285163565a57ef8d3c89254a30685b57d83a38d326c2"
|
||||
|
||||
[[package]]
|
||||
name = "windows_x86_64_gnu"
|
||||
version = "0.52.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78"
|
||||
|
||||
[[package]]
|
||||
name = "windows_x86_64_gnu"
|
||||
version = "0.53.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9c3842cdd74a865a8066ab39c8a7a473c0778a3f29370b5fd6b4b9aa7df4a499"
|
||||
|
||||
[[package]]
|
||||
name = "windows_x86_64_gnullvm"
|
||||
version = "0.52.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d"
|
||||
|
||||
[[package]]
|
||||
name = "windows_x86_64_gnullvm"
|
||||
version = "0.53.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0ffa179e2d07eee8ad8f57493436566c7cc30ac536a3379fdf008f47f6bb7ae1"
|
||||
|
||||
[[package]]
|
||||
name = "windows_x86_64_msvc"
|
||||
version = "0.52.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec"
|
||||
|
||||
[[package]]
|
||||
name = "windows_x86_64_msvc"
|
||||
version = "0.53.1"
|
||||
|
||||
@@ -131,6 +131,7 @@ fast-inc = []
|
||||
fs = ["dunce", "libc", "winapi-util", "windows-sys"]
|
||||
fsext = ["libc", "windows-sys"]
|
||||
fsxattr = ["xattr"]
|
||||
hardware = []
|
||||
lines = []
|
||||
feat_systemd_logind = ["utmpx", "libc"]
|
||||
format = [
|
||||
|
||||
@@ -74,6 +74,8 @@ pub mod tty;
|
||||
|
||||
#[cfg(all(unix, feature = "fsxattr"))]
|
||||
pub mod fsxattr;
|
||||
#[cfg(feature = "hardware")]
|
||||
pub mod hardware;
|
||||
#[cfg(all(target_os = "linux", feature = "selinux"))]
|
||||
pub mod selinux;
|
||||
#[cfg(all(unix, not(target_os = "fuchsia"), feature = "signals"))]
|
||||
|
||||
@@ -0,0 +1,433 @@
|
||||
// This file is part of the uutils coreutils package.
|
||||
//
|
||||
// For the full copyright and license information, please view the LICENSE
|
||||
// file that was distributed with this source code.
|
||||
|
||||
//! CPU hardware capability detection for performance-sensitive utilities
|
||||
//!
|
||||
//! This module provides a unified interface for detecting CPU features and
|
||||
//! respecting environment-based SIMD policies (e.g., GLIBC_TUNABLES).
|
||||
//!
|
||||
//! # Use Cases
|
||||
//!
|
||||
//! - `cksum --debug`: Report hardware acceleration capabilities
|
||||
//! - `wc --debug`: Report SIMD usage and GLIBC_TUNABLES restrictions
|
||||
//! - Runtime decisions: Enable/disable SIMD paths based on environment
|
||||
//!
|
||||
//! # Examples
|
||||
//!
|
||||
//! ```no_run
|
||||
//! use uucore::hardware::{CpuFeatures, simd_policy};
|
||||
//!
|
||||
//! // Simple hardware detection
|
||||
//! let features = CpuFeatures::detect();
|
||||
//! if features.has_avx2() {
|
||||
//! println!("AVX2 is available");
|
||||
//! }
|
||||
//!
|
||||
//! // Check SIMD policy (respects GLIBC_TUNABLES)
|
||||
//! let policy = simd_policy();
|
||||
//! if policy.allows_simd() {
|
||||
//! // Use SIMD-accelerated path
|
||||
//! } else {
|
||||
//! // Fall back to scalar implementation
|
||||
//! }
|
||||
//! ```
|
||||
|
||||
use std::env;
|
||||
use std::sync::OnceLock;
|
||||
|
||||
/// CPU hardware features that affect performance
|
||||
///
|
||||
/// Provides platform-specific CPU feature detection with caching.
|
||||
/// Detection is performed once and cached for the lifetime of the process.
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||
pub struct CpuFeatures {
|
||||
/// AVX-512 support (x86/x86_64 only)
|
||||
avx512: bool,
|
||||
/// AVX2 support (x86/x86_64 only)
|
||||
avx2: bool,
|
||||
/// PCLMULQDQ support for CRC acceleration (x86/x86_64 only)
|
||||
pclmul: bool,
|
||||
/// VMULL support for CRC acceleration (ARM only)
|
||||
vmull: bool,
|
||||
/// SSE2 support (x86/x86_64 only)
|
||||
sse2: bool,
|
||||
/// ARM ASIMD/NEON support (aarch64 only)
|
||||
asimd: bool,
|
||||
}
|
||||
|
||||
impl CpuFeatures {
|
||||
/// Detect available CPU features (cached after first call)
|
||||
///
|
||||
/// This function uses a singleton pattern to ensure feature detection
|
||||
/// happens only once per process. Thread-safe.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```no_run
|
||||
/// use uucore::hardware::CpuFeatures;
|
||||
///
|
||||
/// let features = CpuFeatures::detect();
|
||||
/// println!("AVX2: {}", features.has_avx2());
|
||||
/// ```
|
||||
pub fn detect() -> Self {
|
||||
static FEATURES: OnceLock<CpuFeatures> = OnceLock::new();
|
||||
*FEATURES.get_or_init(Self::detect_impl)
|
||||
}
|
||||
|
||||
fn detect_impl() -> Self {
|
||||
Self {
|
||||
avx512: detect_avx512(),
|
||||
avx2: detect_avx2(),
|
||||
pclmul: detect_pclmul(),
|
||||
vmull: detect_vmull(),
|
||||
sse2: detect_sse2(),
|
||||
asimd: detect_asimd(),
|
||||
}
|
||||
}
|
||||
|
||||
/// Check if AVX-512 is available (x86/x86_64 only)
|
||||
pub fn has_avx512(&self) -> bool {
|
||||
self.avx512
|
||||
}
|
||||
|
||||
/// Check if AVX2 is available (x86/x86_64 only)
|
||||
pub fn has_avx2(&self) -> bool {
|
||||
self.avx2
|
||||
}
|
||||
|
||||
/// Check if PCLMULQDQ is available (x86/x86_64 only)
|
||||
pub fn has_pclmul(&self) -> bool {
|
||||
self.pclmul
|
||||
}
|
||||
|
||||
/// Check if VMULL is available (ARM only)
|
||||
pub fn has_vmull(&self) -> bool {
|
||||
self.vmull
|
||||
}
|
||||
|
||||
/// Check if SSE2 is available (x86/x86_64 only)
|
||||
pub fn has_sse2(&self) -> bool {
|
||||
self.sse2
|
||||
}
|
||||
|
||||
/// Check if ARM ASIMD/NEON is available (aarch64 only)
|
||||
pub fn has_asimd(&self) -> bool {
|
||||
self.asimd
|
||||
}
|
||||
|
||||
/// Get list of available features as strings
|
||||
///
|
||||
/// Returns uppercase feature names (e.g., "AVX2", "SSE2", "ASIMD")
|
||||
pub fn available_features(&self) -> Vec<&'static str> {
|
||||
let mut features = Vec::new();
|
||||
if self.avx512 {
|
||||
features.push("AVX512");
|
||||
}
|
||||
if self.avx2 {
|
||||
features.push("AVX2");
|
||||
}
|
||||
if self.pclmul {
|
||||
features.push("PCLMUL");
|
||||
}
|
||||
if self.vmull {
|
||||
features.push("VMULL");
|
||||
}
|
||||
if self.sse2 {
|
||||
features.push("SSE2");
|
||||
}
|
||||
if self.asimd {
|
||||
features.push("ASIMD");
|
||||
}
|
||||
features
|
||||
}
|
||||
}
|
||||
|
||||
/// SIMD policy based on environment variables
|
||||
///
|
||||
/// Respects GLIBC_TUNABLES environment variable to disable specific CPU features.
|
||||
/// This is used by GNU utilities to allow users to disable hardware acceleration.
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct SimdPolicy {
|
||||
/// Features disabled via GLIBC_TUNABLES (e.g., ["AVX2", "AVX512F"])
|
||||
disabled_by_env: Vec<String>,
|
||||
/// Hardware features actually available
|
||||
hardware_features: CpuFeatures,
|
||||
}
|
||||
|
||||
impl SimdPolicy {
|
||||
/// Create a new SIMD policy by checking environment and hardware
|
||||
fn new() -> Self {
|
||||
let tunables = env::var("GLIBC_TUNABLES").unwrap_or_default();
|
||||
let disabled_by_env = parse_disabled_features(&tunables);
|
||||
let hardware_features = CpuFeatures::detect();
|
||||
|
||||
Self {
|
||||
disabled_by_env,
|
||||
hardware_features,
|
||||
}
|
||||
}
|
||||
|
||||
/// Check if SIMD operations are allowed
|
||||
///
|
||||
/// Returns `false` if any features are disabled via GLIBC_TUNABLES,
|
||||
/// regardless of what's available in hardware.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```no_run
|
||||
/// use uucore::hardware::simd_policy;
|
||||
///
|
||||
/// let policy = simd_policy();
|
||||
/// if policy.allows_simd() {
|
||||
/// // Use SIMD-accelerated bytecount
|
||||
/// } else {
|
||||
/// // Use scalar fallback
|
||||
/// }
|
||||
/// ```
|
||||
pub fn allows_simd(&self) -> bool {
|
||||
self.disabled_by_env.is_empty()
|
||||
}
|
||||
|
||||
/// Get list of features disabled by environment
|
||||
pub fn disabled_features(&self) -> &[String] {
|
||||
&self.disabled_by_env
|
||||
}
|
||||
|
||||
/// Get available hardware features
|
||||
pub fn hardware_features(&self) -> &CpuFeatures {
|
||||
&self.hardware_features
|
||||
}
|
||||
|
||||
/// Get list of features that are both available and not disabled
|
||||
pub fn enabled_features(&self) -> Vec<&'static str> {
|
||||
if !self.allows_simd() {
|
||||
return Vec::new();
|
||||
}
|
||||
self.hardware_features.available_features()
|
||||
}
|
||||
}
|
||||
|
||||
/// Get the global SIMD policy (cached)
|
||||
///
|
||||
/// This checks both hardware capabilities and the GLIBC_TUNABLES environment
|
||||
/// variable. The result is cached for the lifetime of the process.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```no_run
|
||||
/// use uucore::hardware::simd_policy;
|
||||
///
|
||||
/// let policy = simd_policy();
|
||||
/// if policy.allows_simd() {
|
||||
/// println!("SIMD is enabled");
|
||||
/// } else {
|
||||
/// println!("SIMD disabled by: {:?}", policy.disabled_features());
|
||||
/// }
|
||||
/// ```
|
||||
pub fn simd_policy() -> &'static SimdPolicy {
|
||||
static POLICY: OnceLock<SimdPolicy> = OnceLock::new();
|
||||
POLICY.get_or_init(SimdPolicy::new)
|
||||
}
|
||||
|
||||
// Platform-specific feature detection
|
||||
|
||||
#[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
|
||||
fn detect_avx512() -> bool {
|
||||
if cfg!(target_os = "android") {
|
||||
false
|
||||
} else {
|
||||
std::arch::is_x86_feature_detected!("avx512f")
|
||||
&& std::arch::is_x86_feature_detected!("avx512bw")
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(not(any(target_arch = "x86", target_arch = "x86_64")))]
|
||||
fn detect_avx512() -> bool {
|
||||
false
|
||||
}
|
||||
|
||||
#[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
|
||||
fn detect_avx2() -> bool {
|
||||
if cfg!(target_os = "android") {
|
||||
false
|
||||
} else {
|
||||
std::arch::is_x86_feature_detected!("avx2")
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(not(any(target_arch = "x86", target_arch = "x86_64")))]
|
||||
fn detect_avx2() -> bool {
|
||||
false
|
||||
}
|
||||
|
||||
#[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
|
||||
fn detect_pclmul() -> bool {
|
||||
if cfg!(target_os = "android") {
|
||||
false
|
||||
} else {
|
||||
std::arch::is_x86_feature_detected!("pclmulqdq")
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(not(any(target_arch = "x86", target_arch = "x86_64")))]
|
||||
fn detect_pclmul() -> bool {
|
||||
false
|
||||
}
|
||||
|
||||
#[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
|
||||
fn detect_sse2() -> bool {
|
||||
if cfg!(target_os = "android") {
|
||||
false
|
||||
} else {
|
||||
std::arch::is_x86_feature_detected!("sse2")
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(not(any(target_arch = "x86", target_arch = "x86_64")))]
|
||||
fn detect_sse2() -> bool {
|
||||
false
|
||||
}
|
||||
|
||||
#[cfg(all(target_arch = "aarch64", target_endian = "little"))]
|
||||
fn detect_asimd() -> bool {
|
||||
if cfg!(target_os = "android") {
|
||||
false
|
||||
} else {
|
||||
std::arch::is_aarch64_feature_detected!("asimd")
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(not(all(target_arch = "aarch64", target_endian = "little")))]
|
||||
fn detect_asimd() -> bool {
|
||||
false
|
||||
}
|
||||
|
||||
#[cfg(target_arch = "aarch64")]
|
||||
fn detect_vmull() -> bool {
|
||||
// VMULL is part of ARM NEON/ASIMD
|
||||
// For now, we use ASIMD as a proxy
|
||||
detect_asimd()
|
||||
}
|
||||
|
||||
#[cfg(not(target_arch = "aarch64"))]
|
||||
fn detect_vmull() -> bool {
|
||||
false
|
||||
}
|
||||
|
||||
// GLIBC_TUNABLES parsing
|
||||
|
||||
/// Parse GLIBC_TUNABLES environment variable for disabled features
|
||||
///
|
||||
/// Format: `glibc.cpu.hwcaps=-AVX2,-AVX512F`
|
||||
/// Multiple tunable sections can be separated by colons.
|
||||
fn parse_disabled_features(tunables: &str) -> Vec<String> {
|
||||
if tunables.is_empty() {
|
||||
return Vec::new();
|
||||
}
|
||||
|
||||
let mut disabled = Vec::new();
|
||||
|
||||
// GLIBC_TUNABLES format: "tunable1=value1:tunable2=value2"
|
||||
for entry in tunables.split(':') {
|
||||
let entry = entry.trim();
|
||||
let Some((name, raw_value)) = entry.split_once('=') else {
|
||||
continue;
|
||||
};
|
||||
|
||||
// We only care about glibc.cpu.hwcaps
|
||||
if name.trim() != "glibc.cpu.hwcaps" {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Parse comma-separated features, disabled ones start with '-'
|
||||
for token in raw_value.split(',') {
|
||||
let token = token.trim();
|
||||
if let Some(feature) = token.strip_prefix('-') {
|
||||
let feature = feature.trim().to_ascii_uppercase();
|
||||
if !feature.is_empty() {
|
||||
disabled.push(feature);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
disabled
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn test_cpu_features_detection() {
|
||||
let features = CpuFeatures::detect();
|
||||
// Just verify it doesn't panic and returns consistent results
|
||||
let features2 = CpuFeatures::detect();
|
||||
assert_eq!(features, features2);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_available_features() {
|
||||
let features = CpuFeatures::detect();
|
||||
let available = features.available_features();
|
||||
// Should return a list (may be empty on some platforms)
|
||||
assert!(available.iter().all(|s| !s.is_empty()));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_parse_disabled_features_empty() {
|
||||
assert_eq!(parse_disabled_features(""), Vec::<String>::new());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_parse_disabled_features_single() {
|
||||
let result = parse_disabled_features("glibc.cpu.hwcaps=-AVX2");
|
||||
assert_eq!(result, vec!["AVX2"]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_parse_disabled_features_multiple() {
|
||||
let result = parse_disabled_features("glibc.cpu.hwcaps=-AVX2,-AVX512F");
|
||||
assert_eq!(result, vec!["AVX2", "AVX512F"]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_parse_disabled_features_mixed() {
|
||||
let result = parse_disabled_features("glibc.cpu.hwcaps=-AVX2,SSE2,-AVX512F");
|
||||
// Only features with '-' prefix are disabled
|
||||
assert_eq!(result, vec!["AVX2", "AVX512F"]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_parse_disabled_features_with_other_tunables() {
|
||||
let result =
|
||||
parse_disabled_features("glibc.malloc.check=1:glibc.cpu.hwcaps=-AVX2:other=value");
|
||||
assert_eq!(result, vec!["AVX2"]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_parse_disabled_features_case_insensitive() {
|
||||
let result = parse_disabled_features("glibc.cpu.hwcaps=-avx2,-Avx512f");
|
||||
// Should normalize to uppercase
|
||||
assert_eq!(result, vec!["AVX2", "AVX512F"]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_simd_policy() {
|
||||
let policy = simd_policy();
|
||||
// Just verify it works
|
||||
let _ = policy.allows_simd();
|
||||
let _ = policy.disabled_features();
|
||||
let _ = policy.enabled_features();
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_simd_policy_caching() {
|
||||
let policy1 = simd_policy();
|
||||
let policy2 = simd_policy();
|
||||
// Should be same instance (pointer equality)
|
||||
assert!(std::ptr::eq(policy1, policy2));
|
||||
}
|
||||
}
|
||||
@@ -54,6 +54,8 @@ pub use crate::features::fast_inc;
|
||||
pub use crate::features::format;
|
||||
#[cfg(feature = "fs")]
|
||||
pub use crate::features::fs;
|
||||
#[cfg(feature = "hardware")]
|
||||
pub use crate::features::hardware;
|
||||
#[cfg(feature = "i18n-common")]
|
||||
pub use crate::features::i18n;
|
||||
#[cfg(feature = "lines")]
|
||||
|
||||
Reference in New Issue
Block a user