├── .envrc ├── .github ├── dependabot.yml └── workflows │ └── ci.yml ├── .gitignore ├── .pre-commit-config.yaml ├── Cargo.lock ├── Cargo.toml ├── LICENSE ├── README.md ├── flake.lock ├── flake.nix ├── rust-toolchain.toml └── src ├── blanket.rs ├── lib.rs └── x86.rs /.envrc: -------------------------------------------------------------------------------- 1 | use flake 2 | 3 | watch_file rust-toolchain.toml 4 | 5 | eval "$shellHook" 6 | -------------------------------------------------------------------------------- /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | version: 2 2 | updates: 3 | - package-ecosystem: cargo 4 | directory: "/" 5 | schedule: 6 | interval: daily 7 | time: "13:00" 8 | open-pull-requests-limit: 10 9 | -------------------------------------------------------------------------------- /.github/workflows/ci.yml: -------------------------------------------------------------------------------- 1 | name: ci 2 | on: 3 | push: 4 | branches: 5 | - master 6 | pull_request: 7 | 8 | env: 9 | flake: github:${{ github.repository }}/${{ github.sha }} 10 | 11 | jobs: 12 | pre-commit: 13 | runs-on: ubuntu-latest 14 | steps: 15 | - uses: actions/checkout@v4 16 | - uses: DeterminateSystems/nix-installer-action@v12 17 | - name: pre-commit-run 18 | run: nix develop --command -- pre-commit run --all-files 19 | nix-build: 20 | runs-on: ubuntu-latest 21 | steps: 22 | - uses: DeterminateSystems/nix-installer-action@v12 23 | - name: nix build 24 | run: nix build -L '${{ env.flake }}' 25 | - name: nix flake check 26 | run: nix flake check -L '${{ env.flake }}' 27 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | ### Rust ### 2 | # Generated by Cargo 3 | # will have compiled files and executables 4 | debug/ 5 | target/ 6 | 7 | # Remove Cargo.lock from gitignore if creating an executable, leave it for libraries 8 | # More information here https://doc.rust-lang.org/cargo/guide/cargo-toml-vs-cargo-lock.html 9 | # Cargo.lock 10 | 11 | # These are backup files generated by rustfmt 12 | **/*.rs.bk 13 | 14 | # MSVC Windows builds of rustc generate these, which store debugging information 15 | *.pdb 16 | 17 | ### Nix ### 18 | result 19 | result-* 20 | *.drv 21 | .direnv 22 | -------------------------------------------------------------------------------- /.pre-commit-config.yaml: -------------------------------------------------------------------------------- 1 | repos: 2 | - repo: https://github.com/pre-commit/pre-commit-hooks 3 | rev: v4.5.0 4 | hooks: 5 | - id: check-added-large-files 6 | - id: check-case-conflict 7 | - id: check-merge-conflict 8 | - id: check-toml 9 | - id: check-vcs-permalinks 10 | - id: check-yaml 11 | - id: destroyed-symlinks 12 | - id: detect-private-key 13 | - id: end-of-file-fixer 14 | - id: fix-byte-order-marker 15 | - id: mixed-line-ending 16 | - id: trailing-whitespace 17 | 18 | - repo: local 19 | hooks: 20 | - id: cargo-fmt 21 | name: cargo fmt 22 | entry: cargo fmt 23 | types: [rust] 24 | language: system 25 | pass_filenames: false 26 | - id: cargo-check 27 | name: cargo check 28 | entry: cargo check 29 | types: [rust] 30 | language: system 31 | pass_filenames: false 32 | - id: cargo-clippy 33 | name: cargo clippy 34 | entry: cargo clippy -- --deny warnings 35 | types: [rust] 36 | language: system 37 | pass_filenames: false 38 | - id: nixpkgs-fmt 39 | entry: nixpkgs-fmt 40 | types: [nix] 41 | language: system 42 | name: nixpkgs-fmt 43 | - id: statix 44 | entry: statix check 45 | types: [nix] 46 | language: system 47 | name: statix 48 | -------------------------------------------------------------------------------- /Cargo.lock: -------------------------------------------------------------------------------- 1 | # This file is automatically @generated by Cargo. 2 | # It is not intended for manual editing. 3 | version = 3 4 | 5 | [[package]] 6 | name = "bitflags" 7 | version = "2.5.0" 8 | source = "registry+https://github.com/rust-lang/crates.io-index" 9 | checksum = "cf4b9d6a944f767f8e5e0db018570623c85f3d925ac718db4e06d0187adb21c1" 10 | 11 | [[package]] 12 | name = "cache-size" 13 | version = "0.7.0" 14 | dependencies = [ 15 | "raw-cpuid", 16 | ] 17 | 18 | [[package]] 19 | name = "raw-cpuid" 20 | version = "11.1.0" 21 | source = "registry+https://github.com/rust-lang/crates.io-index" 22 | checksum = "cb9ee317cfe3fbd54b36a511efc1edd42e216903c9cd575e686dd68a2ba90d8d" 23 | dependencies = [ 24 | "bitflags", 25 | ] 26 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | authors = ["Bernardo Meurer "] 3 | description = "A library for finding your L1/L2/L3 cache sizes " 4 | version = "0.7.0" 5 | 6 | categories = ["hardware-support", "memory-management"] 7 | edition = "2018" 8 | keywords = ["memory", "performance", "line", "size", "cache"] 9 | license = "BSD-3-Clause" 10 | name = "cache-size" 11 | readme = "./README.md" 12 | repository = "https://github.com/lovesegfault/cache-size" 13 | 14 | [badges] 15 | maintenance = { status = "actively-developed" } 16 | 17 | [target.'cfg(target_arch = "x86")'.dependencies] 18 | raw-cpuid = "11" 19 | [target.'cfg(target_arch = "x86_64")'.dependencies] 20 | raw-cpuid = "11" 21 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | BSD 3-Clause License 2 | 3 | Copyright (c) 2018, Bernardo Meurer 4 | All rights reserved. 5 | 6 | Redistribution and use in source and binary forms, with or without 7 | modification, are permitted provided that the following conditions are met: 8 | 9 | * Redistributions of source code must retain the above copyright notice, this 10 | list of conditions and the following disclaimer. 11 | 12 | * Redistributions in binary form must reproduce the above copyright notice, 13 | this list of conditions and the following disclaimer in the documentation 14 | and/or other materials provided with the distribution. 15 | 16 | * Neither the name of the copyright holder nor the names of its 17 | contributors may be used to endorse or promote products derived from 18 | this software without specific prior written permission. 19 | 20 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 21 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 22 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 23 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 24 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 25 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 26 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 27 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 28 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 29 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 30 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # cache-size 2 | [![License](https://img.shields.io/badge/License-BSD%203--Clause-blue.svg)](https://opensource.org/licenses/BSD-3-Clause) [![Crates.io](https://img.shields.io/crates/v/cache-size.svg)](https://crates.io/crates/cache-size) [![Documentation](https://docs.rs/cache-size/badge.svg)](https://docs.rs/cache-size) ![Continuous Integration](https://github.com/lovesegfault/cache-size/workflows/Continuous%20Integration/badge.svg) 3 | 4 | A library to quickly get the size and line size of your CPU caches. 5 | 6 | Currently this crate only supports x86 CPUs, since it relies on the `CPUID` instruction, via 7 | the [`raw_cpuid`][raw_cpuid] crate. It is a goal to support other architectures; PRs are 8 | welcome! 9 | 10 | Note that the library will still compile and work on non x86 architectures, but 11 | the result of all the cache queries will be `None`. 12 | 13 | --- 14 | Check the [Intel 64 and IA-32 Architectures Software Developers Manual](https://software.intel.com/sites/default/files/managed/39/c5/325462-sdm-vol-1-2abcd-3abcd.pdf) 15 | for more information on the `CPUID` instruction. 16 | 17 | [raw_cpuid]: https://github.com/gz/rust-cpuid 18 | -------------------------------------------------------------------------------- /flake.lock: -------------------------------------------------------------------------------- 1 | { 2 | "nodes": { 3 | "crane": { 4 | "inputs": { 5 | "nixpkgs": [ 6 | "nixpkgs" 7 | ] 8 | }, 9 | "locked": { 10 | "lastModified": 1719249093, 11 | "narHash": "sha256-0q1haa3sw6GbmJ+WhogMnducZGjEaCa/iR6hF2vq80I=", 12 | "owner": "ipetkov", 13 | "repo": "crane", 14 | "rev": "9791c77eb7e98b8d8ac5b0305d47282f994411ca", 15 | "type": "github" 16 | }, 17 | "original": { 18 | "owner": "ipetkov", 19 | "repo": "crane", 20 | "type": "github" 21 | } 22 | }, 23 | "nixpkgs": { 24 | "locked": { 25 | "lastModified": 1719075281, 26 | "narHash": "sha256-CyyxvOwFf12I91PBWz43iGT1kjsf5oi6ax7CrvaMyAo=", 27 | "owner": "NixOS", 28 | "repo": "nixpkgs", 29 | "rev": "a71e967ef3694799d0c418c98332f7ff4cc5f6af", 30 | "type": "github" 31 | }, 32 | "original": { 33 | "owner": "NixOS", 34 | "ref": "nixos-unstable", 35 | "repo": "nixpkgs", 36 | "type": "github" 37 | } 38 | }, 39 | "root": { 40 | "inputs": { 41 | "crane": "crane", 42 | "nixpkgs": "nixpkgs", 43 | "rust": "rust", 44 | "utils": "utils" 45 | } 46 | }, 47 | "rust": { 48 | "inputs": { 49 | "nixpkgs": [ 50 | "nixpkgs" 51 | ] 52 | }, 53 | "locked": { 54 | "lastModified": 1719195554, 55 | "narHash": "sha256-bFXHMjpYlEERexzXa1gLGJO/1l8dxaAtSNE56YALuTg=", 56 | "owner": "oxalica", 57 | "repo": "rust-overlay", 58 | "rev": "577ee84c69ba89894ac622d71a678a14d746b2f7", 59 | "type": "github" 60 | }, 61 | "original": { 62 | "owner": "oxalica", 63 | "repo": "rust-overlay", 64 | "type": "github" 65 | } 66 | }, 67 | "systems": { 68 | "locked": { 69 | "lastModified": 1681028828, 70 | "narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=", 71 | "owner": "nix-systems", 72 | "repo": "default", 73 | "rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e", 74 | "type": "github" 75 | }, 76 | "original": { 77 | "owner": "nix-systems", 78 | "repo": "default", 79 | "type": "github" 80 | } 81 | }, 82 | "utils": { 83 | "inputs": { 84 | "systems": "systems" 85 | }, 86 | "locked": { 87 | "lastModified": 1710146030, 88 | "narHash": "sha256-SZ5L6eA7HJ/nmkzGG7/ISclqe6oZdOZTNoesiInkXPQ=", 89 | "owner": "numtide", 90 | "repo": "flake-utils", 91 | "rev": "b1d9ab70662946ef0850d488da1c9019f3a9752a", 92 | "type": "github" 93 | }, 94 | "original": { 95 | "owner": "numtide", 96 | "repo": "flake-utils", 97 | "type": "github" 98 | } 99 | } 100 | }, 101 | "root": "root", 102 | "version": 7 103 | } 104 | -------------------------------------------------------------------------------- /flake.nix: -------------------------------------------------------------------------------- 1 | { 2 | description = " A Rust library to quickly get the size and line size of your CPU caches"; 3 | 4 | inputs = { 5 | nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable"; 6 | crane = { 7 | url = "github:ipetkov/crane"; 8 | inputs.nixpkgs.follows = "nixpkgs"; 9 | }; 10 | rust = { 11 | url = "github:oxalica/rust-overlay"; 12 | inputs.nixpkgs.follows = "nixpkgs"; 13 | }; 14 | utils.url = "github:numtide/flake-utils"; 15 | }; 16 | 17 | outputs = { self, nixpkgs, crane, rust, utils, ... }: utils.lib.eachDefaultSystem (system: 18 | let 19 | pkgs = import nixpkgs { inherit system; overlays = [ rust.overlays.default ]; }; 20 | 21 | rustToolchain = pkgs.rust-bin.fromRustupToolchainFile ./rust-toolchain.toml; 22 | 23 | craneLib = (crane.mkLib pkgs).overrideToolchain rustToolchain; 24 | 25 | src = ./.; 26 | 27 | cargoArtifacts = craneLib.buildDepsOnly { 28 | inherit src; 29 | }; 30 | fmt = craneLib.cargoFmt { inherit cargoArtifacts src; }; 31 | clippy = craneLib.cargoClippy { 32 | inherit src; 33 | cargoArtifacts = fmt; 34 | cargoClippyExtraArgs = "-- --deny warnings"; 35 | }; 36 | crate = craneLib.buildPackage { 37 | inherit src; 38 | cargoArtifacts = clippy; 39 | }; 40 | in 41 | { 42 | checks = { inherit fmt clippy; }; 43 | packages.default = crate; 44 | devShells.default = with pkgs; mkShell { 45 | name = "cache-size"; 46 | nativeBuildInputs = [ 47 | rustToolchain 48 | cargo-edit 49 | cargo-udeps 50 | cargo-watch 51 | rust-analyzer 52 | 53 | nixpkgs-fmt 54 | nil 55 | statix 56 | 57 | pre-commit 58 | ]; 59 | shellHook = '' 60 | pre-commit install --install-hooks 61 | ''; 62 | }; 63 | }); 64 | } 65 | -------------------------------------------------------------------------------- /rust-toolchain.toml: -------------------------------------------------------------------------------- 1 | [toolchain] 2 | channel = "stable" 3 | components = [ 4 | "cargo", 5 | "clippy", 6 | "rust-analysis", 7 | "rust-src", 8 | "rust-std", 9 | "rustc", 10 | "rustfmt", 11 | ] 12 | targets = [ 13 | "x86_64-unknown-linux-musl", 14 | "x86_64-pc-windows-gnu", 15 | "x86_64-apple-darwin" 16 | ] 17 | -------------------------------------------------------------------------------- /src/blanket.rs: -------------------------------------------------------------------------------- 1 | /// Info about a what a given cache caches (instructions, data, etc.) 2 | #[derive(PartialEq, Eq, Debug)] 3 | pub enum CacheType { 4 | /// Null - No more caches 5 | Null = 0, 6 | /// Data cache 7 | Data, 8 | /// Instruction cache 9 | Instruction, 10 | /// Data and Instruction cache 11 | Unified, 12 | /// 4-31 = Reserved 13 | Reserved, 14 | } 15 | 16 | /// Returns the total size in bytes of `level` cache with type `cache_type`. 17 | /// 18 | /// This is the implementation for unsupported architectures, and always returns None. 19 | #[inline] 20 | pub fn cache_size(_level: u8, _cache_type: CacheType) -> Option { 21 | None 22 | } 23 | 24 | /// Returns the line size in bytes of `level` cache with type `cache_type`. 25 | /// 26 | /// This is the implementation for unsupported architectures, and always returns None. 27 | #[inline] 28 | pub fn cache_line_size(_level: u8, _cache_type: CacheType) -> Option { 29 | None 30 | } 31 | 32 | /// Returns the total size in bytes of of the L1 data cache. 33 | /// 34 | /// This is the implementation for unsupported architectures, and always returns None. 35 | #[inline] 36 | pub fn l1_cache_size() -> Option { 37 | None 38 | } 39 | 40 | /// Returns the line size in bytes of the L1 data cache. 41 | /// 42 | /// This is the implementation for unsupported architectures, and always returns None. 43 | #[inline] 44 | pub fn l1_cache_line_size() -> Option { 45 | cache_line_size(1, CacheType::Data) 46 | } 47 | 48 | /// Returns the total size in bytes of the unified L2 cache. 49 | /// 50 | /// This is the implementation for unsupported architectures, and always returns None. 51 | #[inline] 52 | pub fn l2_cache_size() -> Option { 53 | cache_size(2, CacheType::Unified) 54 | } 55 | 56 | /// Returns the line size in bytes of the unified L2 cache. 57 | /// 58 | /// This is the implementation for unsupported architectures, and always returns None. 59 | #[inline] 60 | pub fn l2_cache_line_size() -> Option { 61 | cache_line_size(2, CacheType::Unified) 62 | } 63 | 64 | /// Returns the total size in bytes of the unified L3 cache. 65 | /// 66 | /// This is the implementation for unsupported architectures, and always returns None. 67 | #[inline] 68 | pub fn l3_cache_size() -> Option { 69 | cache_size(3, CacheType::Unified) 70 | } 71 | 72 | /// Returns the line size in bytes of the unified L3 cache. 73 | /// 74 | /// This is the implementation for unsupported architectures, and always returns None. 75 | #[inline] 76 | pub fn l3_cache_line_size() -> Option { 77 | cache_line_size(3, CacheType::Unified) 78 | } 79 | 80 | #[cfg(test)] 81 | mod tests { 82 | use super::*; 83 | #[test] 84 | fn test_l1_cache_size() { 85 | assert_eq!(l1_cache_size(), None); 86 | } 87 | #[test] 88 | fn test_l1_cache_line_size() { 89 | assert_eq!(l1_cache_line_size(), None) 90 | } 91 | #[test] 92 | fn test_l2_cache_size() { 93 | assert_eq!(l2_cache_size(), None); 94 | } 95 | #[test] 96 | fn test_l2_cache_line_size() { 97 | assert_eq!(l2_cache_line_size(), None); 98 | } 99 | #[test] 100 | fn test_l3_cache_size() { 101 | assert_eq!(l3_cache_size(), None); 102 | } 103 | #[test] 104 | fn test_l3_cache_line_size() { 105 | assert_eq!(l3_cache_line_size(), None); 106 | } 107 | } 108 | -------------------------------------------------------------------------------- /src/lib.rs: -------------------------------------------------------------------------------- 1 | //! A library to quickly get the size and line size of your CPU caches. 2 | //! 3 | //! Currently this crate only supports x86 CPUs, since it relies on the `CPUID` instruction, via 4 | //! the [`raw_cpuid`](raw_cpuid) crate. It is a goal to support other architectures; PRs are 5 | //! welcome! 6 | //! 7 | //! Note that the library will still compile and work on non x86 architectures, but the result of 8 | //! all the cache queries will be `None`. 9 | //! 10 | //! Check the [Intel 64 and IA-32 Architectures Software Developers Manual](https://software.intel.com/sites/default/files/managed/39/c5/325462-sdm-vol-1-2abcd-3abcd.pdf) 11 | //! for more information on the `CPUID` instruction. 12 | 13 | #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] 14 | mod x86; 15 | #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] 16 | pub use x86::*; 17 | 18 | #[cfg(not(any(target_arch = "x86", target_arch = "x86_64")))] 19 | mod blanket; 20 | #[cfg(not(any(target_arch = "x86", target_arch = "x86_64")))] 21 | pub use blanket::*; 22 | -------------------------------------------------------------------------------- /src/x86.rs: -------------------------------------------------------------------------------- 1 | use raw_cpuid::{self, CpuId, CpuIdReaderNative}; 2 | 3 | pub use raw_cpuid::CacheType; 4 | 5 | /// Uses the CPUID family info to detect Zen architecture CPUs. 6 | /// 7 | /// Data pulled from https://en.wikichip.org/wiki/amd/cpuid. 8 | #[inline] 9 | fn amd_is_zen(cpuid: &CpuId) -> Option { 10 | let info = cpuid.get_feature_info()?; 11 | match (info.base_family_id(), info.extended_family_id()) { 12 | (0xF, 0x8..=0xA) => Some(true), 13 | _ => Some(false), 14 | } 15 | } 16 | 17 | /// Uses cache parameters to get cache size at a given level with the provided cache type. 18 | #[inline] 19 | fn generic_cache_size( 20 | cpuid: CpuId, 21 | level: u8, 22 | cache_type: CacheType, 23 | ) -> Option { 24 | cpuid 25 | .get_cache_parameters()? 26 | .filter(|c| c.level() == level && c.cache_type() == cache_type) 27 | .map(|c| c.sets() * c.associativity() * c.coherency_line_size()) 28 | .min() 29 | } 30 | 31 | /// This is computed using tlb info. The values come back in kilobytes, so they are multiplied by 32 | /// 1024 to give the size in bytes to match the behaviour of other architectures. 33 | #[inline] 34 | fn amd_cache_size( 35 | cpuid: CpuId, 36 | level: u8, 37 | cache_type: CacheType, 38 | ) -> Option { 39 | match (level, cache_type) { 40 | (1, CacheType::Instruction) => cpuid 41 | .get_l1_cache_and_tlb_info() 42 | .map(|i| i.icache_size() as usize * 1024), 43 | (1, CacheType::Data) => cpuid 44 | .get_l1_cache_and_tlb_info() 45 | .map(|i| i.icache_size() as usize * 1024), 46 | (2, CacheType::Unified) => cpuid 47 | .get_l2_l3_cache_and_tlb_info() 48 | .map(|i| i.l2cache_size() as usize * 1024), 49 | (3, CacheType::Unified) => cpuid 50 | .get_l2_l3_cache_and_tlb_info() 51 | .map(|i| i.l3cache_size() as usize * 1024), 52 | _ => None, 53 | } 54 | } 55 | 56 | /// Returns the total size in bytes of `level` cache with type `cache_type`. 57 | /// 58 | /// The only possibilities for this returning `None` are if the system does not support cache 59 | /// parameters, in which case [`get_cache_parameters()`](raw_cpuid::CpuId::get_cache_parameters) will 60 | /// fail, or if the selected cache level and/or type does not exist. 61 | /// 62 | /// On an AMD Zen architecture this is computed using tlb info. The values come back in kilobytes, 63 | /// so they are multiplied by 1024 to give the size in bytes to match the behaviour of other 64 | /// architectures. 65 | /// 66 | /// On other architectures this is computed as `associativity * line_size * sets`, and if there are multiple caches 67 | /// available, it returns the size of the **smallest** cache. 68 | #[inline] 69 | pub fn cache_size(level: u8, cache_type: CacheType) -> Option { 70 | let cpuid = CpuId::new(); 71 | match cpuid.get_vendor_info()?.as_str() { 72 | "AuthenticAMD" if amd_is_zen(&cpuid).unwrap_or(false) => { 73 | amd_cache_size(cpuid, level, cache_type) 74 | } 75 | _ => generic_cache_size(cpuid, level, cache_type), 76 | } 77 | } 78 | 79 | /// Uses cache parameters to get cache line size at a given level with the provided cache type. 80 | #[inline] 81 | fn generic_cache_line_size( 82 | cpuid: CpuId, 83 | level: u8, 84 | cache_type: CacheType, 85 | ) -> Option { 86 | cpuid 87 | .get_cache_parameters()? 88 | .filter(|cparams| cparams.level() == level && cparams.cache_type() == cache_type) 89 | .map(|cparams| cparams.coherency_line_size()) 90 | .min() 91 | } 92 | 93 | /// This is computed using tlb info. Instruction and data cache line sizes 94 | /// are available separately for the L1 cache, but only unified is available for L2 and L3 caches. 95 | #[inline] 96 | fn amd_cache_line_size( 97 | cpuid: CpuId, 98 | level: u8, 99 | cache_type: CacheType, 100 | ) -> Option { 101 | match (level, cache_type) { 102 | (1, CacheType::Instruction) => cpuid 103 | .get_l1_cache_and_tlb_info() 104 | .map(|i| i.icache_line_size() as usize), 105 | (1, CacheType::Data) => cpuid 106 | .get_l1_cache_and_tlb_info() 107 | .map(|i| i.dcache_line_size() as usize), 108 | (2, CacheType::Unified) => cpuid 109 | .get_l2_l3_cache_and_tlb_info() 110 | .map(|i| i.l2cache_line_size() as usize), 111 | (3, CacheType::Unified) => cpuid 112 | .get_l2_l3_cache_and_tlb_info() 113 | .map(|i| i.l3cache_line_size() as usize), 114 | _ => None, 115 | } 116 | } 117 | 118 | /// Returns the line size in bytes of `level` cache with type `cache_type`. 119 | /// 120 | /// The only possibilities for this returning `None` are if the system does not support cache 121 | /// parameters, in which case [`get_cache_parameters()`](raw_cpuid::CpuId::get_cache_parameters) will 122 | /// fail, or if the selected cache level and/or type does not exist. 123 | /// 124 | /// On an AMD Zen architecture this is computed using tlb info. Instruction and data cache line 125 | /// sizes are available separately for the L1 cache, but only unified is available for L2 and L3 126 | /// caches. 127 | /// 128 | /// On other x86 architectures this is computed from 129 | /// [`coherency_line_size()`](raw_cpuid::CacheParameter::coherency_line_size), 130 | /// and if there are multiple caches available, it returns the size of the **smallest** cache. 131 | #[inline] 132 | pub fn cache_line_size(level: u8, cache_type: CacheType) -> Option { 133 | let cpuid = CpuId::new(); 134 | match cpuid.get_vendor_info()?.as_str() { 135 | "AuthenticAMD" if amd_is_zen(&cpuid).unwrap_or(false) => { 136 | amd_cache_line_size(cpuid, level, cache_type) 137 | } 138 | _ => generic_cache_line_size(cpuid, level, cache_type), 139 | } 140 | } 141 | 142 | /// Returns the total size in bytes of the L1 data cache. 143 | /// 144 | /// The only possibilities for this returning `None` are if the system does not support cache 145 | /// parameters, in which case [`get_cache_parameters()`](raw_cpuid::CpuId::get_cache_parameters) will 146 | /// fail, or if the system has no L1 data cache (if it's a unified L1 cache for example). 147 | /// 148 | /// This is computed as `associativity * line_size * sets`, and if there are multiple caches 149 | /// available, it returns the size of the **smallest** cache. 150 | #[inline] 151 | pub fn l1_cache_size() -> Option { 152 | cache_size(1, CacheType::Data) 153 | } 154 | 155 | /// Returns the line size in bytes of the L1 data cache. 156 | /// 157 | /// The only possibilities for this returning `None` are if the system does not support cache 158 | /// parameters, in which case [`get_cache_parameters()`](raw_cpuid::CpuId::get_cache_parameters) will 159 | /// fail, or if the system has no L1 data cache (if it's a unified L1 cache for example). 160 | /// 161 | /// This is computed from [`coherency_line_size()`](raw_cpuid::CacheParameter::coherency_line_size), 162 | /// and if there are multiple caches available, it returns the size of the **smallest** cache. 163 | #[inline] 164 | pub fn l1_cache_line_size() -> Option { 165 | cache_line_size(1, CacheType::Data) 166 | } 167 | 168 | /// Returns the total size in bytes of the unified L2 cache. 169 | /// 170 | /// The only possibilities for this returning `None` are if the system does not support cache 171 | /// parameters, in which case [`get_cache_parameters()`](raw_cpuid::CpuId::get_cache_parameters) will 172 | /// fail, or if the system has no unified L2 cache. 173 | /// 174 | /// This is computed as `associativity * line_size * sets`, and if there are multiple caches 175 | /// available, it returns the size of the **smallest** cache. 176 | #[inline] 177 | pub fn l2_cache_size() -> Option { 178 | cache_size(2, CacheType::Unified) 179 | } 180 | 181 | /// Returns the line size in bytes of the unified L2 cache. 182 | /// 183 | /// The only possibilities for this returning `None` are if the system does not support cache 184 | /// parameters, in which case [`get_cache_parameters()`](raw_cpuid::CpuId::get_cache_parameters) will 185 | /// fail, or if the system has no unified L2 cache. 186 | /// 187 | /// This is computed from [`coherency_line_size()`](raw_cpuid::CacheParameter::coherency_line_size), 188 | /// and if there are multiple caches available, it returns the size of the **smallest** cache. 189 | #[inline] 190 | pub fn l2_cache_line_size() -> Option { 191 | cache_line_size(2, CacheType::Unified) 192 | } 193 | 194 | /// Returns the total size in bytes of the unified L3 cache. 195 | /// 196 | /// The only possibilities for this returning `None` are if the system does not support cache 197 | /// parameters, in which case [`get_cache_parameters()`](raw_cpuid::CpuId::get_cache_parameters) will 198 | /// fail, or if the system has no unified L3 cache. 199 | /// 200 | /// This is computed as `associativity * line_size * sets`, and if there are multiple caches 201 | /// available, it returns the size of the **smallest** cache. 202 | #[inline] 203 | pub fn l3_cache_size() -> Option { 204 | cache_size(3, CacheType::Unified) 205 | } 206 | 207 | /// Returns the line size in bytes of the unified L3 cache. 208 | /// 209 | /// The only possibilities for this returning `None` are if the system does not support cache 210 | /// parameters, in which case [`get_cache_parameters()`](raw_cpuid::CpuId::get_cache_parameters) will 211 | /// fail, or if the system has no unified L3 cache. 212 | /// 213 | /// This is computed from [`coherency_line_size()`](raw_cpuid::CacheParameter::coherency_line_size), 214 | /// and if there are multiple caches available, it returns the size of the **smallest** cache. 215 | #[inline] 216 | pub fn l3_cache_line_size() -> Option { 217 | cache_line_size(3, CacheType::Unified) 218 | } 219 | 220 | /// Tests 221 | /// 222 | /// I'd like to figure out a way to test this crate, but it's behavior being entirely 223 | /// hardware-dependant that seems hard, if not impossible. 224 | #[cfg(test)] 225 | mod tests {} 226 | --------------------------------------------------------------------------------