#!/usr/bin/env dub
/+ dub.sdl:
name "cpu_pmu_mem_latency_numa"
platforms "linux"
targetPath "build"
+/
/**
* Precise memory-access sampling with data-source + NUMA-node attribution, pure D.
*
* The *precise data-source* concern of the analysis spine, exercised end to end
* on the one engine that implements it on this host — AMD **IBS** (Instruction-
* Based Sampling). It:
*
* 1. Opens the `ibs_op` PMU (its dynamic `type` read from
* `/sys/bus/event_source/devices/ibs_op/type`) with
* `PERF_SAMPLE_IP | ADDR | DATA_SRC | WEIGHT` (+ `PHYS_ADDR` when the host
* permits it), then strides a buffer larger than L3 to provoke DRAM loads.
* The kernel forwards a core-PMU `precise_ip` request to exactly this PMU
* (`forward_event_to_ibs`, `arch/x86/events/amd/ibs.c`); AMD has **no**
* PEBS, so the `cpu` PMU reports `max_precise == 0` and IBS is the *only*
* precise engine — the `cpu`/`precise_ip` fallback below is Intel-only and
* stays unexercised here.
* 2. Decodes each sample's `perf_mem_data_src` union (the same bitfields the
* IBS driver fills from `IBS_OP_DATA2`/`DATA3`) into human level/op/snoop/
* TLB strings, matching `tools/perf/util/mem-events.c`.
* 3. Classifies each sampled *data* address to a NUMA node two ways —
* `get_mempolicy(MPOL_F_NODE | MPOL_F_ADDR)` and `move_pages()` query mode
* (raw syscalls; see the note on libnuma below) — and compares them against
* the workload buffer's home node. This box is single-node, so every
* address resolves to node 0: the API round-trip is demonstrated, the
* cross-node *classification* is not (recorded as a host limit).
*
* `get_mempolicy`/`move_pages` are NOT in glibc (they live in libnuma, whose
* functions are themselves thin syscall wrappers) and numactl ships no
* `numa.pc`, so a `libs "numa"` link would be a hard build-time dependency that
* a host without libnuma cannot satisfy — defeating "green on any host". We call
* the two syscalls directly through the libc `syscall(2)` wrapper instead: no
* external C library, identical kernel path.
*
* Companion to docs/research/cpu-pmu/precise-sampling.md
* § "Data-source & data-address sampling: AMD IBS" and
* § "From data address to NUMA node".
*
* Run with: dub run --single mem-latency-numa.d
*
* Environment recorded: Linux 6.18.26, AMD Ryzen 9 7940HX (Zen 4, family 0x19
* model 0x61; `ibs_op` type 11, `zen4_ibs_extensions=1`), single NUMA node,
* `/proc/sys/kernel/perf_event_paranoid` = -1, numactl 2.0.19 (headers only —
* not linked), LDC 1.41 druntime `core.sys.linux.perf_event`.
*
* Portability: no precise PMU (no IBS and `cpu` `max_precise == 0`), a refused
* `perf_event_open` (`perf_event_paranoid`, seccomp), no data-address samples,
* or a non-Linux/non-NUMA host each print a `SKIP:` or reduced line and exit 0,
* so CI stays green on any host.
*/
module (module) cpu_pmu_mem_latency_numaPrecise memory-access sampling with data-source + NUMA-node attribution, pure D.
The precise data-source concern of the analysis spine, exercised end to end
on the one engine that implements it on this host — AMD IBS (Instruction-
Based Sampling). It:
Opens the ibs_op PMU (its dynamic type read from
/sys/bus/event_source/devices/ibs_op/type) with
PERF_SAMPLE_IP | ADDR | DATA_SRC | WEIGHT (+ PHYS_ADDR when the host
permits it), then strides a buffer larger than L3 to provoke DRAM loads.
The kernel forwards a core-PMU precise_ip request to exactly this PMU
(forward_event_to_ibs, arch/x86/events/amd/ibs.c); AMD has no
PEBS, so the cpu PMU reports max_precise == 0 and IBS is the only
precise engine — the cpu/precise_ip fallback below is Intel-only and
stays unexercised here.
Decodes each sample's perf_mem_data_src union (the same bitfields the
IBS driver fills from IBS_OP_DATA2/DATA3) into human level/op/snoop/
TLB strings, matching tools/perf/util/mem-events.c.
Classifies each sampled data address to a NUMA node two ways —
get_mempolicy(MPOL_F_NODE | MPOL_F_ADDR) and move_pages() query mode
(raw syscalls; see the note on libnuma below) — and compares them against
the workload buffer's home node. This box is single-node, so every
address resolves to node 0: the API round-trip is demonstrated, the
cross-node classification is not (recorded as a host limit).
get_mempolicy/move_pages are NOT in glibc (they live in libnuma, whose
functions are themselves thin syscall wrappers) and numactl ships no
numa.pc, so a libs "numa" link would be a hard build-time dependency that
a host without libnuma cannot satisfy — defeating "green on any host". We call
the two syscalls directly through the libc syscall(2) wrapper instead: no
external C library, identical kernel path.
Companion to docs/research/cpu-pmu/precise-sampling.md
§ "Data-source & data-address sampling: AMD IBS" and
§ "From data address to NUMA node".
Run with: dub run --single mem-latency-numa.d
Environment recorded: Linux 6.18.26, AMD Ryzen 9 7940HX (Zen 4, family 0x19
model 0x61; ibs_op type 11, zen4_ibs_extensions=1), single NUMA node,
/proc/sys/kernel/perf_event_paranoid = -1, numactl 2.0.19 (headers only —
not linked), LDC 1.41 druntime core.sys.linux.perf_event.
Portability
no precise PMU (no IBS and cpu max_precise == 0), a refused
perf_event_open (perf_event_paranoid, seccomp), no data-address samples,
or a non-Linux/non-NUMA host each print a SKIP: or reduced line and exit 0,
so CI stays green on any host.
cpu_pmu_mem_latency_numa;
version (linuxlinux)
{
import (package) corecore.(package) core.syssys.(package) core.sys.linuxlinux.(module) core.sys.linux.perf_eventD header file for perf_event_open system call.
Converted from linux userspace header, comments included.
perf_event;
import (package) corecore.(package) core.syssys.(package) core.sys.posixposix.(module) core.sys.posix.unistdD header file for POSIX.
unistd : (alias) cpu_pmu_mem_latency_numa.close = int core.sys.posix.unistd.close(int) nothrow @nogc @trustedclose, (alias) cpu_pmu_mem_latency_numa.sysconf = long core.sys.posix.unistd.sysconf(int) nothrow @nogc @trustedsysconf, (alias enum value) cpu_pmu_mem_latency_numa._SC_PAGESIZE = core.sys.posix.unistd._SC_PAGESIZE = 30_SC_PAGESIZE;
import (package) corecore.(package) core.syssys.(package) core.sys.posixposix.(package) core.sys.posix.syssys.(module) core.sys.posix.sys.mmanD header file for POSIX.
mman : mmap, (alias) cpu_pmu_mem_latency_numa.munmap = int core.sys.posix.sys.mman.munmap(void*, ulong) nothrow @nogcmunmap, (alias constant) cpu_pmu_mem_latency_numa.PROT_READ = int core.sys.posix.sys.mman.PROT_READ = 1PROT_READ, (alias constant) cpu_pmu_mem_latency_numa.PROT_WRITE = int core.sys.posix.sys.mman.PROT_WRITE = 2PROT_WRITE,
(alias constant) cpu_pmu_mem_latency_numa.MAP_SHARED = int core.sys.posix.sys.mman.MAP_SHARED = 1MAP_SHARED, (alias constant) cpu_pmu_mem_latency_numa.MAP_PRIVATE = int core.sys.posix.sys.mman.MAP_PRIVATE = 2MAP_PRIVATE, (alias constant) cpu_pmu_mem_latency_numa.MAP_FAILED = void* core.sys.posix.sys.mman.MAP_FAILED = cast(void*)cast(size_t)18446744073709551615LUMAP_FAILED;
import (package) corecore.(module) core.atomicThe atomic module provides basic support for lock-free
concurrent programming.
Use the -preview=nosharedaccess compiler flag to detect
unsafe individual read or write operations on shared data.
Source
core/atomic.d
Examples
int y = 2;
shared int x = y; // OK
//x++; // read modify write error
x.atomicOp!"+="(1); // OK
//y = x; // read error with preview flag
y = x.atomicLoad(); // OK
assert(y == 3);
//x = 5; // write error with preview flag
x.atomicStore(5); // OK
assert(x.atomicLoad() == 5);
atomic : (alias template) cpu_pmu_mem_latency_numa.atomicLoad = core.atomic.atomicLoad(MemoryOrder ms = MemoryOrder.seq, T)(auto ref return scope const T val) if (!is(T == shared(U), U) && !is(T == shared(inout(U)), U) && !is(T == shared(const(U)), U))Loads 'val' from memory and returns it. The memory barrier specified
by 'ms' is applied to the operation, which is fully sequenced by
default. Valid memory orders are MemoryOrder.raw, MemoryOrder.acq,
and MemoryOrder.seq.
atomicLoad, (alias template) cpu_pmu_mem_latency_numa.atomicStore = core.atomic.atomicStore(MemoryOrder ms = MemoryOrder.seq, T, V)(ref T val, V newval) if (!is(T == shared) && !is(V == shared))Writes 'newval' into 'val'. The memory barrier specified by 'ms' is
applied to the operation, which is fully sequenced by default.
Valid memory orders are MemoryOrder.raw, MemoryOrder.rel, and
MemoryOrder.seq.
atomicStore, (enum) core.atomic.MemoryOrderSpecifies the memory ordering semantics of an atomic operation.
MemoryOrder;
import (package) stdstd.(module) std.stdioCategory Symbols File handles _popen File isFileHandle openNetwork stderr stdin stdout Reading chunks lines readf readfln readln Writing toFile write writef writefln writeln Misc KeepTerminator LockType StdioException
Standard I/O functions that extend core.stdc.stdio. core.stdc.stdio
is publically imported when importing std.stdio.
There are three layers of I/O:
The lowest layer is the operating system layer. The two main schemes are Windows and Posix.
C's stdio.h which unifies the two operating system schemes.
std.stdio, this module, unifies the various stdio.h implementations into
a high level package for D programs.
Source
std/stdio.d
stdio : (alias template) cpu_pmu_mem_latency_numa.writefln = std.stdio.writefln(alias fmt, A...)(A args) if (isSomeString!(typeof(fmt)))Equivalent to writef(fmt, args, '\n').
writefln, (alias template) cpu_pmu_mem_latency_numa.writeln = std.stdio.writeln(T...)(T args)Equivalent to write(args, '\n'). Calling writeln without
arguments is valid and just prints a newline to the standard
output.
Example
Reads stdin and writes it to stdout with an argument
counter.
import std.stdio;
void main()
{
string line;
for (size_t count = 0; (line = readln) !is null; count++)
{
writeln("Input ", count, ": ", line);
}
}
writeln;
import (package) stdstd.(module) std.algorithmThis package implements generic algorithms oriented towards the processing of
sequences. Sequences processed by these functions define range-based
interfaces. See also Reference on ranges and
tutorial on ranges.
Algorithms are categorized into the following submodules:
Submodule Functions
| Searching |
all
any
balancedParens
boyerMooreFinder
canFind
commonPrefix
count
countUntil
endsWith
find
findAdjacent
findAmong
findSkip
findSplit
findSplitAfter
findSplitBefore
minCount
maxCount
minElement
maxElement
minIndex
maxIndex
minPos
maxPos
skipOver
startsWith
until
|
| Comparison |
among
castSwitch
clamp
cmp
either
equal
isPermutation
isSameLength
levenshteinDistance
levenshteinDistanceAndPath
max
min
mismatch
predSwitch
|
| Iteration |
cache
cacheBidirectional
chunkBy
cumulativeFold
each
filter
filterBidirectional
fold
group
joiner
map
mean
permutations
reduce
splitWhen
splitter
substitute
sum
uniq
|
| Sorting |
completeSort
isPartitioned
isSorted
isStrictlyMonotonic
ordered
strictlyOrdered
makeIndex
merge
multiSort
nextEvenPermutation
nextPermutation
nthPermutation
partialSort
partition
partition3
schwartzSort
sort
topN
topNCopy
topNIndex
|
| Set operations (setops) |
cartesianProduct
largestPartialIntersection
largestPartialIntersectionWeighted
multiwayMerge
multiwayUnion
setDifference
setIntersection
setSymmetricDifference
|
| Mutation |
bringToFront
copy
fill
initializeAll
move
moveAll
moveSome
moveEmplace
moveEmplaceAll
moveEmplaceSome
remove
reverse
strip
stripLeft
stripRight
swap
swapRanges
uninitializedFill
|
Many functions in this package are parameterized with a predicate.
The predicate may be any suitable callable type
(a function, a delegate, a functor, or a lambda), or a
compile-time string. The string may consist of any legal D
expression that uses the symbol a (for unary functions) or the
symbols a and b (for binary functions). These names will NOT
interfere with other homonym symbols in user code because they are
evaluated in a different context. The default for all binary
comparison predicates is "a == b" for unordered operations and
"a < b" for ordered operations.
Example
int[] a = ...;
static bool greater(int a, int b)
{
return a > b;
}
sort!greater(a); // predicate as alias
sort!((a, b) => a > b)(a); // predicate as a lambda.
sort!"a > b"(a); // predicate as string
// (no ambiguity with array name)
sort(a); // no predicate, "a < b" is implicit
Source
std/algorithm/package.d
algorithm : (alias template) cpu_pmu_mem_latency_numa.sort = std.algorithm.sorting.sort(alias less = "a < b", SwapStrategy ss = SwapStrategy.unstable, Range)(Range r)Sorts a random-access range according to the predicate less.
Performs O(r.length * log(r.length)) evaluations of less. If less involves
expensive computations on the sort key, it may be worthwhile to use
schwartzSort instead.
Stable sorting requires hasAssignableElements!Range to be true.
sort returns a SortedRange over the original range,
allowing functions that can take advantage of sorted data to know that the
range is sorted and adjust accordingly. The SortedRange is a
wrapper around the original range, so both it and the original range are sorted.
Other functions can't know that the original range has been sorted, but
they can know that SortedRange has been sorted.
Preconditions
The predicate is expected to satisfy certain rules in order for sort to
behave as expected - otherwise, the program may fail on certain inputs (but not
others) when not compiled in release mode, due to the cursory assumeSorted
check. Specifically, sort expects less(a,b) && less(b,c) to imply
less(a,c) (transitivity), and, conversely, !less(a,b) && !less(b,c) to
imply !less(a,c). Note that the default predicate ("a < b") does not
always satisfy these conditions for floating point types, because the expression
will always be false when either a or b is NaN.
Use cmp instead.
Algorithms
Introsort is used for unstable sorting and
Timsort is used for stable sorting.
Each algorithm has benefits beyond stability. Introsort is generally faster but
Timsort may achieve greater speeds on data with low entropy or if predicate calls
are expensive. Introsort performs no allocations whereas Timsort will perform one
or more allocations per call. Both algorithms have O(n log n) worst-case
time complexity.
sort, (alias template) cpu_pmu_mem_latency_numa.min = std.algorithm.comparison.min(T...)(T args) if (T.length >= 2 && !is(CommonType!T == void))Iterates the passed arguments and returns the minimum value.
min;
// ---- libc / kernel seams -------------------------------------------------
// Anonymous mapping for the workload buffer (Linux value; the druntime posix
// binding does not export MAP_ANON uniformly).
enum (constant) int cpu_pmu_mem_latency_numa.MAP_ANON = 32MAP_ANON = 0x20;
// PERF_SAMPLE_* live in a named D enum, so hoist the few this probe uses to
// unqualified manifest constants for readable sample_type expressions.
enum (constant) core.sys.linux.perf_event.perf_event_sample_format cpu_pmu_mem_latency_numa.PERF_SAMPLE_IP = perf_event_sample_format.PERF_SAMPLE_IPPERF_SAMPLE_IP = (enum) core.sys.linux.perf_event.perf_event_sample_formatBits that can be set in attr.sample_type to request information
in the overflow packets.
perf_event_sample_format.(enum value) core.sys.linux.perf_event.perf_event_sample_format.PERF_SAMPLE_IP = 1uPERF_SAMPLE_IP;
enum (constant) core.sys.linux.perf_event.perf_event_sample_format cpu_pmu_mem_latency_numa.PERF_SAMPLE_ADDR = perf_event_sample_format.PERF_SAMPLE_ADDRPERF_SAMPLE_ADDR = (enum) core.sys.linux.perf_event.perf_event_sample_formatBits that can be set in attr.sample_type to request information
in the overflow packets.
perf_event_sample_format.(enum value) core.sys.linux.perf_event.perf_event_sample_format.PERF_SAMPLE_ADDR = 8uPERF_SAMPLE_ADDR;
enum (constant) core.sys.linux.perf_event.perf_event_sample_format cpu_pmu_mem_latency_numa.PERF_SAMPLE_WEIGHT = perf_event_sample_format.PERF_SAMPLE_WEIGHTPERF_SAMPLE_WEIGHT = (enum) core.sys.linux.perf_event.perf_event_sample_formatBits that can be set in attr.sample_type to request information
in the overflow packets.
perf_event_sample_format.(enum value) core.sys.linux.perf_event.perf_event_sample_format.PERF_SAMPLE_WEIGHT = 16384uPERF_SAMPLE_WEIGHT;
enum (constant) core.sys.linux.perf_event.perf_event_sample_format cpu_pmu_mem_latency_numa.PERF_SAMPLE_DATA_SRC = perf_event_sample_format.PERF_SAMPLE_DATA_SRCPERF_SAMPLE_DATA_SRC = (enum) core.sys.linux.perf_event.perf_event_sample_formatBits that can be set in attr.sample_type to request information
in the overflow packets.
perf_event_sample_format.(enum value) core.sys.linux.perf_event.perf_event_sample_format.PERF_SAMPLE_DATA_SRC = 32768uPERF_SAMPLE_DATA_SRC;
enum (constant) core.sys.linux.perf_event.perf_event_sample_format cpu_pmu_mem_latency_numa.PERF_SAMPLE_PHYS_ADDR = perf_event_sample_format.PERF_SAMPLE_PHYS_ADDRPERF_SAMPLE_PHYS_ADDR = (enum) core.sys.linux.perf_event.perf_event_sample_formatBits that can be set in attr.sample_type to request information
in the overflow packets.
perf_event_sample_format.(enum value) core.sys.linux.perf_event.perf_event_sample_format.PERF_SAMPLE_PHYS_ADDR = 524288uPERF_SAMPLE_PHYS_ADDR;
// get_mempolicy(2) / move_pages(2) via the libc syscall wrapper. Numbers are
// per-arch; verified against arch/x86/entry/syscalls/syscall_64.tbl (239 /
// 279) and include/uapi/asm-generic/unistd.h (236 / 239) in the 7.1-rc6 tree.
extern (C) long long cpu_pmu_mem_latency_numa.syscall(long number, ...) nothrow @nogcsyscall(long (parameter) long numbernumber, ...) @nogc nothrow;
version (X86_64X86_64) { enum (constant) int cpu_pmu_mem_latency_numa.SYS_get_mempolicy = 239SYS_get_mempolicy = 239; enum (constant) int cpu_pmu_mem_latency_numa.SYS_move_pages = 279SYS_move_pages = 279; enum (constant) bool cpu_pmu_mem_latency_numa.nodeSyscalls = truenodeSyscalls = true; }
else version (AArch64){ enum SYS_get_mempolicy = 236; enum SYS_move_pages = 239; enum nodeSyscalls = true; }
else version (RISCV64){ enum SYS_get_mempolicy = 236; enum SYS_move_pages = 239; enum nodeSyscalls = true; }
else { enum SYS_get_mempolicy = 0; enum SYS_move_pages = 0; enum nodeSyscalls = false; }
// set_mempolicy(2)/get_mempolicy(2) flags — include/uapi/linux/mempolicy.h.
enum (constant) int cpu_pmu_mem_latency_numa.MPOL_F_NODE = 1MPOL_F_NODE = 1 << 0; // return the node of `addr` (with MPOL_F_ADDR)
enum (constant) int cpu_pmu_mem_latency_numa.MPOL_F_ADDR = 2MPOL_F_ADDR = 1 << 1; // look the vma up by address
// Pin to one CPU so IBS per-thread sampling and the "home node" are stable.
extern (C) int int cpu_pmu_mem_latency_numa.sched_setaffinity(int pid, ulong cpusetsize, const(void)* mask) nothrow @nogcsched_setaffinity(int (parameter) int pidpid, (alias) object.size_t = ulongsize_t (parameter) ulong cpusetsizecpusetsize, const(void)* (parameter) const(void)* maskmask) @nogc nothrow;
// ---- perf_mem_data_src decode (include/uapi/linux/perf_event.h) ----------
//
// The union is one u64 of contiguous bitfields; the shifts below are the
// documented field positions. Constant names/values are the PERF_MEM_*
// macros; the strings mirror tools/perf/util/mem-events.c so the decode
// reads the same as `perf report -D`.
ulong ulong cpu_pmu_mem_latency_numa.bits(ulong v, uint shift, uint width) pure nothrow @nogc @safebits(ulong (parameter) ulong vv, uint (parameter) uint shiftshift, uint (parameter) uint widthwidth) @safe pure nothrow @nogc
=> ((parameter) ulong vv >> (parameter) uint shiftshift) & ((1UL << (parameter) uint widthwidth) - 1);
(alias) object.string = stringstring string cpu_pmu_mem_latency_numa.memOpStr(ulong ds) pure nothrow @nogc @safememOpStr(ulong (parameter) ulong dsds) @safe pure nothrow @nogc
{
const (local variable) const(ulong) opop = ulong cpu_pmu_mem_latency_numa.bits(ulong v, uint shift, uint width) pure nothrow @nogc @safebits((parameter) ulong dsds, 0, 5);
if ((local variable) const(ulong) opop & 0x02) return "LOAD";
if ((local variable) const(ulong) opop & 0x04) return "STORE";
if ((local variable) const(ulong) opop & 0x08) return "PFETCH";
if ((local variable) const(ulong) opop & 0x10) return "EXEC";
return "N/A";
}
// Composite level via mem_lvl_num (shift 33) + remote (37) + hops (43),
// the path perf_mem__lvl_scnprintf takes when lvl_num is set.
(alias) object.string = stringstring string cpu_pmu_mem_latency_numa.memLvlStr(ulong ds) nothrow @safememLvlStr(ulong (parameter) ulong dsds) @safe nothrow
{
static immutable (alias) object.string = stringstring[16] (immutable global) immutable(string[16]) cpu_pmu_mem_latency_numa.memLvlStr.lvlnumlvlnum = [
0x1: "L1", 0x2: "L2", 0x3: "L3", 0x4: "L4", 0x5: "L2 MHB",
0x6: "Memory-side Cache", 0x7: "L0", 0x8: "Uncached", 0x9: "CXL",
0xa: "I/O", 0xb: "Any cache", 0xc: "LFB/MAB", 0xd: "RAM",
0xe: "PMEM", 0xf: "N/A",
];
static immutable (alias) object.string = stringstring[5] (immutable global) immutable(string[5]) cpu_pmu_mem_latency_numa.memLvlStr.hopshops = [
"N/A", "core, same node", "node, same socket",
"socket, same board", "board",
];
const (local variable) const(ulong) lvllvl = ulong cpu_pmu_mem_latency_numa.bits(ulong v, uint shift, uint width) pure nothrow @nogc @safebits((parameter) ulong dsds, 5, 14);
const (local variable) const(string) hithit = ((local variable) const(ulong) lvllvl & 0x02) ? "hit" : ((local variable) const(ulong) lvllvl & 0x04) ? "miss" : "";
const (local variable) const(uint) numnum = cast(uint) ulong cpu_pmu_mem_latency_numa.bits(ulong v, uint shift, uint width) pure nothrow @nogc @safebits((parameter) ulong dsds, 33, 4);
if ((local variable) const(uint) numnum != 0 && (local variable) const(uint) numnum != 0xf)
{
(alias) object.string = stringstring (local variable) string ss;
if (ulong cpu_pmu_mem_latency_numa.bits(ulong v, uint shift, uint width) pure nothrow @nogc @safebits((parameter) ulong dsds, 37, 1)) (local variable) string ss ~= "Remote ";
const (local variable) const(uint) hh = cast(uint) ulong cpu_pmu_mem_latency_numa.bits(ulong v, uint shift, uint width) pure nothrow @nogc @safebits((parameter) ulong dsds, 43, 3);
if ((local variable) const(uint) hh != 0) (local variable) string ss ~= (immutable global) immutable(string[5]) cpu_pmu_mem_latency_numa.memLvlStr.hopshops[(local variable) const(uint) hh] ~ " ";
(local variable) string ss ~= (immutable global) immutable(string[16]) cpu_pmu_mem_latency_numa.memLvlStr.lvlnumlvlnum[(local variable) const(uint) numnum];
if ((local variable) const(string) hithit.(field) ulong const(string).lengthlength) (local variable) string ss ~= " " ~ (local variable) const(string) hithit;
return (local variable) string ss;
}
return "N/A";
}
(alias) object.string = stringstring string cpu_pmu_mem_latency_numa.snoopStr(ulong ds) pure nothrow @nogc @safesnoopStr(ulong (parameter) ulong dsds) @safe pure nothrow @nogc
{
const (local variable) const(ulong) ss = ulong cpu_pmu_mem_latency_numa.bits(ulong v, uint shift, uint width) pure nothrow @nogc @safebits((parameter) ulong dsds, 19, 5);
if ((local variable) const(ulong) ss & 0x10) return "HitM";
if ((local variable) const(ulong) ss & 0x08) return "Miss";
if ((local variable) const(ulong) ss & 0x04) return "Hit";
if ((local variable) const(ulong) ss & 0x02) return "None";
if (ulong cpu_pmu_mem_latency_numa.bits(ulong v, uint shift, uint width) pure nothrow @nogc @safebits((parameter) ulong dsds, 38, 2) & 0x02) return "Peer";
if (ulong cpu_pmu_mem_latency_numa.bits(ulong v, uint shift, uint width) pure nothrow @nogc @safebits((parameter) ulong dsds, 38, 2) & 0x01) return "Fwd";
return "N/A";
}
(alias) object.string = stringstring string cpu_pmu_mem_latency_numa.tlbStr(ulong ds) pure nothrow @safetlbStr(ulong (parameter) ulong dsds) @safe pure nothrow
{
const (local variable) const(ulong) tt = ulong cpu_pmu_mem_latency_numa.bits(ulong v, uint shift, uint width) pure nothrow @nogc @safebits((parameter) ulong dsds, 26, 7);
const (local variable) const(string) wherewhere = ((local variable) const(ulong) tt & 0x08) ? "L1" : ((local variable) const(ulong) tt & 0x10) ? "L2" : ((local variable) const(ulong) tt & 0x20) ? "walker" : "";
const (local variable) const(string) hmhm = ((local variable) const(ulong) tt & 0x02) ? " hit" : ((local variable) const(ulong) tt & 0x04) ? " miss" : "";
if ((local variable) const(string) wherewhere.(field) ulong const(string).lengthlength) return (local variable) const(string) wherewhere ~ (local variable) const(string) hmhm;
return "N/A";
}
// ---- NUMA node oracles ---------------------------------------------------
/// Node of the page containing `addr`, or a negative -errno, via
/// get_mempolicy(MPOL_F_NODE | MPOL_F_ADDR). numaif.h:
/// long get_mempolicy(int *mode, ulong *nmask, ulong maxnode,
/// void *addr, ulong flags);
/// with these flags, `mode` receives the node number.
int int cpu_pmu_mem_latency_numa.nodeViaGetMempolicy(void* addr) nothrow @nogc @trustedNode of the page containing addr, or a negative -errno, via
get_mempolicy(MPOL_F_NODE | MPOL_F_ADDR). numaif.h:
long get_mempolicy(int *mode, ulong nmask, ulong maxnode,
void addr, ulong flags);
with these flags, mode receives the node number.
nodeViaGetMempolicy(void* (parameter) void* addraddr) @trusted @nogc nothrow
{
static if (!nodeSyscalls) return -1;
else
{
int (local variable) int nodenode = -1;
const (local variable) const(long) rr = long cpu_pmu_mem_latency_numa.syscall(long number, ...) nothrow @nogcsyscall((constant) int cpu_pmu_mem_latency_numa.SYS_get_mempolicy = 239SYS_get_mempolicy, &(local variable) int nodenode, null, 0UL, (parameter) void* addraddr,
cast(ulong)((constant) int cpu_pmu_mem_latency_numa.MPOL_F_NODE = 1MPOL_F_NODE | (constant) int cpu_pmu_mem_latency_numa.MPOL_F_ADDR = 2MPOL_F_ADDR));
return (local variable) const(long) rr == 0 ? (local variable) int nodenode : cast(int) (local variable) const(long) rr;
}
}
/// Node of the page containing `addr` via move_pages() query mode (nodes ==
/// NULL). numaif.h:
/// long move_pages(int pid, ulong count, void **pages,
/// const int *nodes, int *status, int flags);
/// `status[0]` receives the node number (or a negative -errno).
int int cpu_pmu_mem_latency_numa.nodeViaMovePages(void* addr, ulong pageSize) nothrow @nogc @trustedNode of the page containing addr via move_pages() query mode (nodes ==
NULL). numaif.h:
long move_pages(int pid, ulong count, void **pages,
const int nodes, int status, int flags);
status[0] receives the node number (or a negative -errno).
nodeViaMovePages(void* (parameter) void* addraddr, (alias) object.size_t = ulongsize_t (parameter) ulong pageSizepageSize) @trusted @nogc nothrow
{
static if (!nodeSyscalls) return -1;
else
{
void* (local variable) void* pagepage = cast(void*)(cast((alias) object.size_t = ulongsize_t) (parameter) void* addraddr & ~((parameter) ulong pageSizepageSize - 1));
int (local variable) int statusstatus = int.(constant) int int.min = -2147483648min;
const (local variable) const(long) rr = long cpu_pmu_mem_latency_numa.syscall(long number, ...) nothrow @nogcsyscall((constant) int cpu_pmu_mem_latency_numa.SYS_move_pages = 279SYS_move_pages, 0, 1UL, &(local variable) void* pagepage, null, &(local variable) int statusstatus, 0UL);
return (local variable) const(long) rr == 0 ? (local variable) int statusstatus : cast(int) (local variable) const(long) rr;
}
}
// ---- sysfs helper --------------------------------------------------------
/// Reads a small unsigned integer from a sysfs file, or -1 on any failure.
long long cpu_pmu_mem_latency_numa.readSysLong(string path) @trustedReads a small unsigned integer from a sysfs file, or -1 on any failure.
readSysLong((alias) object.string = stringstring (parameter) string pathpath) @trusted
{
import (package) stdstd.(module) std.fileUtilities for manipulating files and scanning directories. Functions
in this module handle files as a unit, e.g., read or write one file
at a time. For opening files and manipulating them via handles refer
to module std.stdio.
Category Functions General exists isDir isFile isSymlink rename thisExePath Directories chdir dirEntries getcwd mkdir mkdirRecurse rmdir rmdirRecurse tempDir Files append copy read readText remove slurp write Symlinks symlink readLink Attributes attrIsDir attrIsFile attrIsSymlink getAttributes getLinkAttributes getSize setAttributes Timestamp getTimes getTimesWin setTimes timeLastModified timeLastAccessed timeStatusChanged Other DirEntry FileException PreserveAttributes SpanMode getAvailableDiskSpace
Source
std/file.d
file : (alias template) readText = std.file.readText(S = string, R)(auto ref R name) if (isSomeString!S && (isSomeFiniteCharInputRange!R || is(StringTypeOf!R)))Reads and validates (using $(REF validate, std, utf)) a text file. S can be
an array of any character type. However, no width or endian conversions are
performed. So, if the width or endianness of the characters in the given
file differ from the width or endianness of the element type of S, then
validation will fail.
Params:
S = the string type of the file
name = string or range of characters representing the file _name
Returns: Array of characters read.
Throws: $(LREF FileException) if there is an error reading the file,
$(REF UTFException, std, utf) on UTF decoding error.
See_Also: $(REF read, std,file) for reading a binary file.
readText;
import (package) stdstd.(module) std.stringString handling functions.
Category Functions Searching column indexOf indexOfAny indexOfNeither lastIndexOf lastIndexOfAny lastIndexOfNeither Comparison isNumeric Mutation capitalize Pruning and Filling center chomp chompPrefix chop detabber detab entab entabber leftJustify outdent rightJustify strip stripLeft stripRight wrap Substitution abbrev soundex soundexer succ tr translate Miscellaneous assumeUTF fromStringz lineSplitter representation splitLines toStringz Objects of types string, wstring, and dstring are value types and cannot be mutated element-by-element. For using mutation during building strings, use char[], wchar[], or dchar[]. The xxxstring types are preferable because they don't exhibit undesired aliasing, thus making code more robust.
The following functions are publicly imported:
Module Functions Publicly imported functions std.algorithm cmp, std,algorithm,comparison count, std,algorithm,searching endsWith, std,algorithm,searching startsWith, std,algorithm,searching std.array join, std,array replace, std,array replaceInPlace, std,array split, std,array empty, std,array std.format format, std,format sformat, std,format std.uni icmp, std,uni toLower, std,uni toLowerInPlace, std,uni toUpper, std,uni toUpperInPlace, std,uni There is a rich set of functions for string handling defined in other modules. Functions related to Unicode and ASCII are found in std.uni and std.ascii, respectively. Other functions that have a wider generality than just strings can be found in std.algorithm and std.range.
Source
std/string.d
string : (alias template) strip = std.string.strip(Range)(Range str) if (isSomeString!Range || isRandomAccessRange!Range && hasLength!Range && hasSlicing!Range && !isConvertibleToString!Range && isSomeChar!(ElementEncodingType!Range))Strips both leading and trailing whitespace (as defined by
$(REF isWhite, std,uni)) or as specified in the second argument.
Params:
str = string or random access range of characters
chars = string of characters to be stripped
leftChars = string of leading characters to be stripped
rightChars = string of trailing characters to be stripped
Returns:
slice of `str` stripped of leading and trailing whitespace
or characters as specified in the second argument.
See_Also:
Generic stripping on ranges: $(REF _strip, std, algorithm, mutation)
strip;
import (package) stdstd.(module) std.convA one-stop shop for converting values from one type to another.
Category Functions Generic asOriginalType castFrom parse to toChars bitCast Strings text wtext dtext writeText writeWText writeDText hexString Numeric octal roundTo signed unsigned Exceptions ConvException ConvOverflowException
Source
std/conv.d
conv : (alias template) to = std.conv.to(T)The to template converts a value from one type _to another.
The source type is deduced and the target type must be specified, for example the
expression to!int(42.0) converts the number 42 from
double _to int. The conversion is "safe", i.e.,
it checks for overflow; to!int(4.2e10) would throw the
ConvOverflowException exception. Overflow checks are only
inserted when necessary, e.g., to!double(42) does not do
any checking because any int fits in a double.
Conversions from string _to numeric types differ from the C equivalents
atoi() and atol() by checking for overflow and not allowing whitespace.
For conversion of strings _to signed types, the grammar recognized is:
$(PRE $(I Integer):
$(I Sign UnsignedInteger)
$(I UnsignedInteger)
$(I Sign):
$(B +)
$(B -))
For conversion _to unsigned types, the grammar recognized is:
$(PRE $(I UnsignedInteger):
$(I DecimalDigit)
$(I DecimalDigit) $(I UnsignedInteger))
to;
try
return string std.file.readText!(string, string)(ref string name) @safeReads and validates (using validate) a text file. S can be
an array of any character type. However, no width or endian conversions are
performed. So, if the width or endianness of the characters in the given
file differ from the width or endianness of the element type of S, then
validation will fail.
Examples
Read file with UTF-8 text.
write(deleteme, "abc"); // deleteme is the name of a temporary file
scope(exit) remove(deleteme);
string content = readText(deleteme);
assert(content == "abc");
readText((parameter) string pathpath).string std.string.strip!string(string str) pure nothrow @nogc @safeStrips both leading and trailing whitespace (as defined by
isWhite) or as specified in the second argument.
Examples
import std.uni : lineSep, paraSep;
assert(strip(" hello world ") ==
"hello world");
assert(strip("\n\t\v\rhello world\n\t\v\r") ==
"hello world");
assert(strip("hello world") ==
"hello world");
assert(strip([lineSep] ~ "hello world" ~ [lineSep]) ==
"hello world");
assert(strip([paraSep] ~ "hello world" ~ [paraSep]) ==
"hello world");
strip.long std.conv.to!long.to!string(string __param_0) pure @safeThe to template converts a value from one type to another.
The source type is deduced and the target type must be specified, for example the
expression to`!int(42.0)` converts the number 42 from
`double` to `int`. The conversion is "safe", i.e.,
it checks for overflow; to!int(4.2e10) would throw the
ConvOverflowException exception. Overflow checks are only
inserted when necessary, e.g., ``to!double(42) does not do
any checking because any int fits in a double.
Conversions from string to numeric types differ from the C equivalents
atoi() and atol() by checking for overflow and not allowing whitespace.
For conversion of strings to signed types, the grammar recognized is:
Integer:
Sign UnsignedInteger
UnsignedInteger
Sign:
+
-
For conversion to unsigned types, the grammar recognized is:
UnsignedInteger:
DecimalDigit
DecimalDigit UnsignedInteger
Examples
Converting a value to its own type (useful mostly for generic code)
simply returns its argument.
int a = 42;
int b = to!int(a);
double c = to!double(3.14); // c is double with value 3.14
Converting among numeric types is a safe way to cast them around.
Conversions from floating-point types to integral types allow loss of
precision (the fractional part of a floating-point number). The
conversion is truncating towards zero, the same way a cast would
truncate. (To round a floating point value when casting to an
integral, use roundTo.)
import std.exception : assertThrown;
int a = 420;
assert(to!long(a) == a);
assertThrown!ConvOverflowException(to!byte(a));
assert(to!int(4.2e6) == 4200000);
assertThrown!ConvOverflowException(to!uint(-3.14));
assert(to!uint(3.14) == 3);
assert(to!uint(3.99) == 3);
assert(to!int(-3.99) == -3);
When converting strings to numeric types, note that D hexadecimal and binary
literals are not handled. Neither the prefixes that indicate the base, nor the
horizontal bar used to separate groups of digits are recognized. This also
applies to the suffixes that indicate the type.
To work around this, you can specify a radix for conversions involving numbers.
auto str = to!string(42, 16);
assert(str == "2A");
auto i = to!int(str, 16);
assert(i == 42);
Conversions from integral types to floating-point types always
succeed, but might lose accuracy. The largest integers with a
predecessor representable in floating-point format are 2^24-1 for
float, 2^53-1 for double, and 2^64-1 for real (when
real is 80-bit, e.g. on Intel machines).
// 2^24 - 1, largest proper integer representable as float
int a = 16_777_215;
assert(to!int(to!float(a)) == a);
assert(to!int(to!float(-a)) == -a);
Conversion from string types to char types enforces the input
to consist of a single code point, and said code point must
fit in the target type. Otherwise, ConvException is thrown.
import std.exception : assertThrown;
assert(to!char("a") == 'a');
assertThrown(to!char("ñ")); // 'ñ' does not fit into a char
assert(to!wchar("ñ") == 'ñ');
assertThrown(to!wchar("😃")); // '😃' does not fit into a wchar
assert(to!dchar("😃") == '😃');
// Using wstring or dstring as source type does not affect the result
assert(to!char("a"w) == 'a');
assert(to!char("a"d) == 'a');
// Two code points cannot be converted to a single one
assertThrown(to!char("ab"));
Converting an array to another array type works by converting each
element in turn. Associative arrays can be converted to associative
arrays as long as keys and values can in turn be converted.
import std.string : split;
int[] a = [1, 2, 3];
auto b = to!(float[])(a);
assert(b == [1.0f, 2, 3]);
string str = "1 2 3 4 5 6";
auto numbers = to!(double[])(split(str));
assert(numbers == [1.0, 2, 3, 4, 5, 6]);
int[string] c;
c["a"] = 1;
c["b"] = 2;
auto d = to!(double[wstring])(c);
assert(d["a"w] == 1 && d["b"w] == 2);
Conversions operate transitively, meaning that they work on arrays and
associative arrays of any complexity.
This conversion works because to`!short` applies to an `int`, to!wstring
applies to a string, to`!string` applies to a `double`, and
to!(double[]) applies to an int[]. The conversion might throw an
exception because ``to!short might fail the range check.
int[string][double[int[]]] a;
auto b = to!(short[wstring][string[double[]]])(a);
Object-to-object conversions by dynamic casting throw exception when
the source is non-null and the target is null.
import std.exception : assertThrown;
// Testing object conversions
class A {}
class B : A {}
class C : A {}
A a1 = new A, a2 = new B, a3 = new C;
assert(to!B(a2) is a2);
assert(to!C(a3) is a3);
assertThrown!ConvException(to!B(a3));
Stringize conversion from all types is supported.
String to string conversion works for any two string types having
(char, wchar, dchar) character widths and any
combination of qualifiers (mutable, const, or immutable).
Converts array (other than strings) to string.
Each element is converted by calling ``to!T.
Associative array to string conversion.
Each element is converted by calling ``to!T.
Object to string conversion calls toString against the object or
returns "null" if the object is null.
Struct to string conversion calls toString against the struct if
it is defined.
For structs that do not define toString, the conversion to string
produces the list of fields.
Enumerated types are converted to strings as their symbolic names.
Boolean values are converted to "true" or "false".
char, wchar, dchar to a string type.
Unsigned or signed integers to strings.
: Convert integral value to string in radix radix.
radix must be a value from 2 to 36.
value is treated as a signed value only if radix is 10.
The characters A through Z are used to represent values 10 through 36
and their case is determined by the letterCase parameter.
All floating point types to all string types.
Pointer to string conversions convert the pointer to a size_t value.
If pointer is char*, treat it as C-style strings.
In that case, this function is @system.
See formatValue on how toString should be defined.
// Conversion representing dynamic/static array with string
long[] a = [ 1, 3, 5 ];
assert(to!string(a) == "[1, 3, 5]");
// Conversion representing associative array with string
int[string] associativeArray = ["0":1, "1":2];
assert(to!string(associativeArray) == `["0":1, "1":2]` ||
to!string(associativeArray) == `["1":2, "0":1]`);
// char* to string conversion
assert(to!string(cast(char*) null) == "");
assert(to!string("foo\0".ptr) == "foo");
// Conversion reinterpreting void array to string
auto w = "abcx"w;
const(void)[] b = w;
assert(b.length == 8);
auto c = to!(wchar[])(b);
assert(c == "abcx");
Strings can be converted to enum types. The enum member with the same name as the
input string is returned. The comparison is case-sensitive.
A ConvException is thrown if the enum does not have the specified member.
import std.exception : assertThrown;
enum E { a, b, c }
assert(to!E("a") == E.a);
assert(to!E("b") == E.b);
assertThrown!ConvException(to!E("A"));
to!long;
catch ((class) object.ExceptionThe base class of all errors that are safe to catch and handle.
In principle, only thrown objects derived from this class are safe to catch
inside a catch block. Thrown objects not derived from Exception
represent runtime errors that should not be caught, as certain runtime
guarantees may not hold, making it unsafe to continue program execution.
Examples
bool gotCaught;
try
{
throw new Exception("msg");
}
catch (Exception e)
{
gotCaught = true;
assert(e.msg == "msg");
}
assert(gotCaught);
Exception)
return -1;
}
// ---- the workload --------------------------------------------------------
__gshared ulong (__gshared global) ulong cpu_pmu_mem_latency_numa.sinksink;
/// Lay out `words` as one random permutation cycle of its own indices
/// (Sattolo's algorithm → a single cycle). Chasing `i = words[i]` is then a
/// dependent load per step whose target is unpredictable, so on a working set
/// wider than L3 nearly every step misses to DRAM — the classic pointer-chase
/// memory-latency pattern. Also faults every page in before classification.
void void cpu_pmu_mem_latency_numa.buildChase(ulong[] words) @safeLay out words as one random permutation cycle of its own indices
(Sattolo's algorithm → a single cycle). Chasing i = words[i] is then a
dependent load per step whose target is unpredictable, so on a working set
wider than L3 nearly every step misses to DRAM — the classic pointer-chase
memory-latency pattern. Also faults every page in before classification.
buildChase((alias) object.size_t = ulongsize_t[] (parameter) ulong[] wordswords) @safe
{
foreach ((local variable) ulong ii; 0 .. (parameter) ulong[] wordswords.(field) ulong ulong[].lengthlength)
(parameter) ulong[] wordswords[(local variable) ulong ii] = (local variable) ulong ii;
ulong (local variable) ulong rngrng = 0x9E3779B97F4A7C15UL;
for ((alias) object.size_t = ulongsize_t (local variable) ulong ii = (parameter) ulong[] wordswords.(field) ulong ulong[].lengthlength - 1; i > 0; i--)
{
(local variable) ulong rngrng = (local variable) ulong rngrng * 6364136223846793005UL + 1442695040888963407UL;
const (local variable) const(ulong) jj = cast((alias) object.size_t = ulongsize_t)((local variable) ulong rngrng % (local variable) ulong ii); // 0 <= j < i keeps it a single cycle
const (local variable) const(ulong) tt = (parameter) ulong[] wordswords[(local variable) ulong ii];
(parameter) ulong[] wordswords[(local variable) ulong ii] = (parameter) ulong[] wordswords[(local variable) const(ulong) jj];
(parameter) ulong[] wordswords[(local variable) const(ulong) jj] = (local variable) const(ulong) tt;
}
}
/// `steps` dependent chases from `start`; returns the landing index (folded
/// into a __gshared sink by the caller so the loads survive DCE).
(alias) object.size_t = ulongsize_t ulong cpu_pmu_mem_latency_numa.chase(const(ulong[]) words, ulong start, ulong steps) @safesteps dependent chases from start; returns the landing index (folded
into a _gshared sink by the caller so the loads survive DCE).
chase(const (alias) object.size_t = ulongsize_t[] (parameter) const(ulong[]) wordswords, (alias) object.size_t = ulongsize_t (parameter) ulong startstart, (alias) object.size_t = ulongsize_t (parameter) ulong stepssteps) @safe
{
(alias) object.size_t = ulongsize_t (local variable) ulong ii = (parameter) ulong startstart;
foreach ((local variable) ulong __; 0 .. (parameter) ulong stepssteps)
(local variable) ulong ii = (parameter) const(ulong[]) wordswords[(local variable) ulong ii];
return (local variable) ulong ii;
}
// ---- perf ring-buffer reader ---------------------------------------------
/// Copies `n` bytes out of the perf data area at logical offset `logicalTail`
/// (which may straddle the ring's wrap point) into `dst`.
void void cpu_pmu_mem_latency_numa.ringCopy(void* dst, const(ubyte)* dataStart, ulong dataSize, ulong logicalTail, ulong n) @systemCopies n bytes out of the perf data area at logical offset logicalTail
(which may straddle the ring's wrap point) into dst.
ringCopy(void* (parameter) void* dstdst, const(ubyte)* (parameter) const(ubyte)* dataStartdataStart, ulong (parameter) ulong dataSizedataSize, ulong (parameter) ulong logicalTaillogicalTail, (alias) object.size_t = ulongsize_t (parameter) ulong nn) @system
{
auto (local variable) ubyte* dd = cast(ubyte*) (parameter) void* dstdst;
const (local variable) const(ulong) offoff = cast((alias) object.size_t = ulongsize_t)((parameter) ulong logicalTaillogicalTail % (parameter) ulong dataSizedataSize);
foreach ((local variable) ulong ii; 0 .. (parameter) ulong nn)
(local variable) ubyte* dd[(local variable) ulong ii] = (parameter) const(ubyte)* dataStartdataStart[((local variable) const(ulong) offoff + (local variable) ulong ii) % (parameter) ulong dataSizedataSize];
}
/// One decoded PERF_RECORD_SAMPLE (only the fields this probe requests).
struct (struct) cpu_pmu_mem_latency_numa.SampleOne decoded PERF_RECORD_SAMPLE (only the fields this probe requests).
Sample
{
ulong (field) ulong cpu_pmu_mem_latency_numa.Sample.ipip, (field) ulong cpu_pmu_mem_latency_numa.Sample.addraddr, (field) ulong cpu_pmu_mem_latency_numa.Sample.weightweight, (field) ulong cpu_pmu_mem_latency_numa.Sample.dataSrcdataSrc, (field) ulong cpu_pmu_mem_latency_numa.Sample.physAddrphysAddr;
}
int int cpu_pmu_mem_latency_numa.run()run()
{
const (local variable) const(ulong) pageSizepageSize = cast((alias) object.size_t = ulongsize_t) long core.sys.posix.unistd.sysconf(int) nothrow @nogc @trustedsysconf((enum value) core.sys.posix.unistd._SC_PAGESIZE = 30_SC_PAGESIZE);
// ---- locate a precise-sampling PMU ----------------------------------
bool (local variable) bool viaIbsviaIbs = true;
long (local variable) long pmuTypepmuType = long cpu_pmu_mem_latency_numa.readSysLong(string path) @trustedReads a small unsigned integer from a sysfs file, or -1 on any failure.
readSysLong("/sys/bus/event_source/devices/ibs_op/type");
int (local variable) int maxPrecisemaxPrecise = 0;
if ((local variable) long pmuTypepmuType < 0)
{
(local variable) bool viaIbsviaIbs = false;
(local variable) int maxPrecisemaxPrecise = cast(int) long cpu_pmu_mem_latency_numa.readSysLong(string path) @trustedReads a small unsigned integer from a sysfs file, or -1 on any failure.
readSysLong("/sys/bus/event_source/devices/cpu/caps/max_precise");
if ((local variable) int maxPrecisemaxPrecise <= 0)
{
void std.stdio.writefln!(char, int)(in char[] fmt, int __param_1) @safeEquivalent to writef(fmt, args, '\n').
writefln("SKIP: no precise-sampling PMU — no AMD IBS (`ibs_op`) and "
~ "`cpu` max_precise == %d (Intel needs PEBS/precise_ip>0)", (local variable) int maxPrecisemaxPrecise);
return 0;
}
(local variable) long pmuTypepmuType = (enum) core.sys.linux.perf_event.perf_type_idattr.type
perf_type_id.(enum value) core.sys.linux.perf_event.perf_type_id.PERF_TYPE_HARDWARE = 0PERF_TYPE_HARDWARE; // cpu-PMU precise fallback
}
const (local variable) const(bool) zen4zen4 = long cpu_pmu_mem_latency_numa.readSysLong(string path) @trustedReads a small unsigned integer from a sysfs file, or -1 on any failure.
readSysLong("/sys/bus/event_source/devices/ibs_op/caps/zen4_ibs_extensions") == 1;
// ---- NUMA topology --------------------------------------------------
int (local variable) int nodesOnlinenodesOnline = 1;
{
import (package) stdstd.(module) std.fileUtilities for manipulating files and scanning directories. Functions
in this module handle files as a unit, e.g., read or write one file
at a time. For opening files and manipulating them via handles refer
to module std.stdio.
Category Functions General exists isDir isFile isSymlink rename thisExePath Directories chdir dirEntries getcwd mkdir mkdirRecurse rmdir rmdirRecurse tempDir Files append copy read readText remove slurp write Symlinks symlink readLink Attributes attrIsDir attrIsFile attrIsSymlink getAttributes getLinkAttributes getSize setAttributes Timestamp getTimes getTimesWin setTimes timeLastModified timeLastAccessed timeStatusChanged Other DirEntry FileException PreserveAttributes SpanMode getAvailableDiskSpace
Source
std/file.d
file : (alias template) dirEntries = std.file.dirEntries(bool useDIP1000 = dip1000Enabled)(string path, SpanMode mode, bool followSymlink = true)Returns an $(REF_ALTTEXT input range, isInputRange, std,range,primitives)
of DirEntry that lazily iterates a given directory,
also provides two ways of foreach iteration. The iteration variable can be of
type string if only the name is needed, or DirEntry
if additional details are needed. The span _mode dictates how the
directory is traversed. The name of each iterated directory entry
contains the absolute or relative path (depending on pathname).
Note: The order of returned directory entries is as it is provided by the
operating system / filesystem, and may not follow any particular sorting.
Params:
useDIP1000 = used to instantiate this function separately for code with
and without -preview=dip1000 compiler switch, because it
affects the ABI of this function. Set automatically -
don't touch.
path = The directory to iterate over.
If empty, the current directory will be iterated.
pattern = Optional string with wildcards, such as $(RED
"*.d"). When present, it is used to filter the
results by their file name. The supported wildcard
strings are described under $(REF globMatch,
std,_path).
mode = Whether the directory's sub-directories should be
iterated in depth-first post-order ($(LREF depth)),
depth-first pre-order ($(LREF breadth)), or not at all
($(LREF shallow)).
followSymlink = Whether symbolic links which point to directories
should be treated as directories and their contents
iterated over.
Returns:
An $(REF_ALTTEXT input range, isInputRange,std,range,primitives) of
$(LREF DirEntry).
Throws:
$(UL
$(LI $(LREF FileException) if the $(B path) directory does not exist or read permission is denied.)
$(LI $(LREF FileException) if $(B mode) is not `shallow` and a subdirectory cannot be read.)
)
Example:
// Iterate a directory in depth
foreach (string name; dirEntries("destroy/me", SpanMode.depth))
{
remove(name);
}
// Iterate the current directory in breadth
foreach (string name; dirEntries("", SpanMode.breadth))
{
writeln(name);
}
// Iterate a directory and get detailed info about it
foreach (DirEntry e; dirEntries("dmd-testing", SpanMode.breadth))
{
writeln(e.name, "\t", e.size);
}
// Iterate over all *.d files in current directory and all its subdirectories
auto dFiles = dirEntries("", SpanMode.depth).filter!(f => f.name.endsWith(".d"));
foreach (d; dFiles)
writeln(d.name);
// Hook it up with std.parallelism to compile them all in parallel:
foreach (d; parallel(dFiles, 1)) //passes by 1 file to each thread
{
string cmd = "dmd -c " ~ d.name;
writeln(cmd);
std.process.executeShell(cmd);
}
// Iterate over all D source files in current directory and all its
// subdirectories
auto dFiles = dirEntries("","*.{d,di}",SpanMode.depth);
foreach (d; dFiles)
writeln(d.name);
To handle subdirectories with denied read permission, use SpanMode.shallow:
void scan(string path)
{
foreach (DirEntry entry; dirEntries(path, SpanMode.shallow))
{
try
{
writeln(entry.name);
if (entry.isDir)
scan(entry.name);
}
catch (FileException fe) { continue; } // ignore
}
}
scan("");
---
dirEntries, (enum) std.file.SpanModeDictates directory spanning policy for dirEntries (see below).
Examples
import std.algorithm.comparison : equal;
import std.algorithm.iteration : map;
import std.algorithm.sorting : sort;
import std.array : array;
import std.path : buildPath, relativePath;
auto root = deleteme ~ "root";
scope(exit) root.rmdirRecurse;
root.mkdir;
root.buildPath("animals").mkdir;
root.buildPath("animals", "cat").mkdir;
alias removeRoot = (return scope e) => e.relativePath(root);
assert(root.dirEntries(SpanMode.depth).map!removeRoot.equal(
[buildPath("animals", "cat"), "animals"]));
assert(root.dirEntries(SpanMode.breadth).map!removeRoot.equal(
["animals", buildPath("animals", "cat")]));
root.buildPath("plants").mkdir;
assert(root.dirEntries(SpanMode.shallow).array.sort.map!removeRoot.equal(
["animals", "plants"]));
SpanMode, (alias template) exists = std.file.exists(R)(R name) if (isSomeFiniteCharInputRange!R && !isConvertibleToString!R)Determine whether the given file (or directory) _exists.
Params:
name = string or range of characters representing the file _name
Returns:
true if the file name specified as input exists
exists;
import (package) stdstd.(module) std.algorithmThis package implements generic algorithms oriented towards the processing of
sequences. Sequences processed by these functions define range-based
interfaces. See also Reference on ranges and
tutorial on ranges.
Algorithms are categorized into the following submodules:
Submodule Functions
| Searching |
all
any
balancedParens
boyerMooreFinder
canFind
commonPrefix
count
countUntil
endsWith
find
findAdjacent
findAmong
findSkip
findSplit
findSplitAfter
findSplitBefore
minCount
maxCount
minElement
maxElement
minIndex
maxIndex
minPos
maxPos
skipOver
startsWith
until
|
| Comparison |
among
castSwitch
clamp
cmp
either
equal
isPermutation
isSameLength
levenshteinDistance
levenshteinDistanceAndPath
max
min
mismatch
predSwitch
|
| Iteration |
cache
cacheBidirectional
chunkBy
cumulativeFold
each
filter
filterBidirectional
fold
group
joiner
map
mean
permutations
reduce
splitWhen
splitter
substitute
sum
uniq
|
| Sorting |
completeSort
isPartitioned
isSorted
isStrictlyMonotonic
ordered
strictlyOrdered
makeIndex
merge
multiSort
nextEvenPermutation
nextPermutation
nthPermutation
partialSort
partition
partition3
schwartzSort
sort
topN
topNCopy
topNIndex
|
| Set operations (setops) |
cartesianProduct
largestPartialIntersection
largestPartialIntersectionWeighted
multiwayMerge
multiwayUnion
setDifference
setIntersection
setSymmetricDifference
|
| Mutation |
bringToFront
copy
fill
initializeAll
move
moveAll
moveSome
moveEmplace
moveEmplaceAll
moveEmplaceSome
remove
reverse
strip
stripLeft
stripRight
swap
swapRanges
uninitializedFill
|
Many functions in this package are parameterized with a predicate.
The predicate may be any suitable callable type
(a function, a delegate, a functor, or a lambda), or a
compile-time string. The string may consist of any legal D
expression that uses the symbol a (for unary functions) or the
symbols a and b (for binary functions). These names will NOT
interfere with other homonym symbols in user code because they are
evaluated in a different context. The default for all binary
comparison predicates is "a == b" for unordered operations and
"a < b" for ordered operations.
Example
int[] a = ...;
static bool greater(int a, int b)
{
return a > b;
}
sort!greater(a); // predicate as alias
sort!((a, b) => a > b)(a); // predicate as a lambda.
sort!"a > b"(a); // predicate as string
// (no ambiguity with array name)
sort(a); // no predicate, "a < b" is implicit
Source
std/algorithm/package.d
algorithm : (alias template) startsWith = std.algorithm.searching.startsWith(alias pred = (a, b) => a == b, Range, Needles...)(Range doesThisStart, Needles withOneOfThese) if (isInputRange!Range && (Needles.length > 1) && allSatisfy!(canTestStartsWith!(pred, Range), Needles))Checks whether the given
$(REF_ALTTEXT input range, isInputRange, std,range,primitives) starts with (one
of) the given needle(s) or, if no needles are given,
if its front element fulfils predicate pred.
For more information about pred see $(LREF find).
Params:
pred = Predicate to use in comparing the elements of the haystack and the
needle(s). Mandatory if no needles are given.
doesThisStart = The input range to check.
withOneOfThese = The needles against which the range is to be checked,
which may be individual elements or input ranges of elements.
withThis = The single needle to check, which may be either a single element
or an input range of elements.
Returns:
0 if the needle(s) do not occur at the beginning of the given range;
otherwise the position of the matching needle, that is, 1 if the range starts
with withOneOfThese[0], 2 if it starts with withOneOfThese[1], and so
on.
In the case where doesThisStart starts with multiple of the ranges or
elements in withOneOfThese, then the shortest one matches (if there are
two which match which are of the same length (e.g. "a" and 'a'), then
the left-most of them in the argument
list matches).
In the case when no needle parameters are given, return true iff front of
doesThisStart fulfils predicate pred.
startsWith;
import (package) stdstd.(module) std.pathThis module is used to manipulate path strings.
All functions, with the exception of expandTilde (and in some
cases absolutePath and relativePath), are pure
string manipulation functions; they don't depend on any state outside
the program, nor do they perform any actual file system actions.
This has the consequence that the module does not make any distinction
between a path that points to a directory and a path that points to a
file, and it does not know whether or not the object pointed to by the
path actually exists in the file system.
To differentiate between these cases, use isDir and
exists.
Note that on Windows, both the backslash (\) and the slash (/)
are in principle valid directory separators. This module treats them
both on equal footing, but in cases where a new separator is
added, a backslash will be used. Furthermore, the buildNormalizedPath
function will replace all slashes with backslashes on that platform.
In general, the functions in this module assume that the input paths
are well-formed. (That is, they should not contain invalid characters,
they should follow the file system's path format, etc.) The result
of calling a function on an ill-formed path is undefined. When there
is a chance that a path or a file name is invalid (for instance, when it
has been input by the user), it may sometimes be desirable to use the
isValidFilename and isValidPath functions to check
this.
Most functions do not perform any memory allocations, and if a string is
returned, it is usually a slice of an input string. If a function
allocates, this is explicitly mentioned in the documentation.
Category Functions Normalization absolutePath asAbsolutePath asNormalizedPath asRelativePath buildNormalizedPath buildPath chainPath expandTilde Partitioning baseName dirName dirSeparator driveName pathSeparator pathSplitter relativePath rootName stripDrive Validation isAbsolute isDirSeparator isRooted isValidFilename isValidPath Extension defaultExtension extension setExtension stripExtension withDefaultExtension withExtension Other filenameCharCmp filenameCmp globMatch CaseSensitive
Source
std/path.d
path : (alias template) baseName = std.path.baseName(R)(return scope R path) if (isRandomAccessRange!R && hasSlicing!R && isSomeChar!(ElementType!R) && !isSomeString!R)Params:
cs = Whether or not suffix matching is case-sensitive.
path = A path name. It can be a string, or any random-access range of
characters.
suffix = An optional suffix to be removed from the file name.
Returns: The name of the file in the path name, without any leading
directory and with an optional suffix chopped off.
If `suffix` is specified, it will be compared to `path`
using `filenameCmp!cs`,
where `cs` is an optional template parameter determining whether
the comparison is case sensitive or not. See the
$(LREF filenameCmp) documentation for details.
Note:
This function $(I only) strips away the specified suffix, which
doesn't necessarily have to represent an extension.
To remove the extension from a path, regardless of what the extension
is, use $(LREF stripExtension).
To obtain the filename without leading directories and without
an extension, combine the functions like this:
---
assert(baseName(stripExtension("dir/file.ext")) == "file");
---
Standards:
This function complies with
$(LINK2 http://pubs.opengroup.org/onlinepubs/9699919799/utilities/basename.html,
the POSIX requirements for the 'basename' shell utility)
(with suitable adaptations for Windows paths).
baseName;
if (bool std.file.exists!string(string name) nothrow @nogc @safeDetermine whether the given file (or directory) exists.
exists("/sys/devices/system/node"))
{
int (local variable) int cc = 0;
foreach ((local variable) std.file.DirEntry ee; std.file._DirIterator!false std.file.dirEntries!false(string path, std.file.SpanMode mode, bool followSymlink = true) @systemReturns an input range
of DirEntry that lazily iterates a given directory,
also provides two ways of foreach iteration. The iteration variable can be of
type string if only the name is needed, or DirEntry
if additional details are needed. The span mode dictates how the
directory is traversed. The name of each iterated directory entry
contains the absolute or relative path (depending on pathname).
Note
The order of returned directory entries is as it is provided by the
operating system / filesystem, and may not follow any particular sorting.
Example
// Iterate a directory in depth
foreach (string name; dirEntries("destroy/me", SpanMode.depth))
{
remove(name);
}
// Iterate the current directory in breadth
foreach (string name; dirEntries("", SpanMode.breadth))
{
writeln(name);
}
// Iterate a directory and get detailed info about it
foreach (DirEntry e; dirEntries("dmd-testing", SpanMode.breadth))
{
writeln(e.name, "\t", e.size);
}
// Iterate over all *.d files in current directory and all its subdirectories
auto dFiles = dirEntries("", SpanMode.depth).filter!(f => f.name.endsWith(".d"));
foreach (d; dFiles)
writeln(d.name);
// Hook it up with std.parallelism to compile them all in parallel:
foreach (d; parallel(dFiles, 1)) //passes by 1 file to each thread
{
string cmd = "dmd -c " ~ d.name;
writeln(cmd);
std.process.executeShell(cmd);
}
// Iterate over all D source files in current directory and all its
// subdirectories
auto dFiles = dirEntries("","*.{d,di}",SpanMode.depth);
foreach (d; dFiles)
writeln(d.name);
To handle subdirectories with denied read permission, use SpanMode.shallow:
void scan(string path)
{
foreach (DirEntry entry; dirEntries(path, SpanMode.shallow))
{
try
{
writeln(entry.name);
if (entry.isDir)
scan(entry.name);
}
catch (FileException fe) { continue; } // ignore
}
}
scan("");
Examples
Duplicate functionality of D1's std.file.listdir():
string[] listdir(string pathname)
{
import std.algorithm.iteration : map, filter;
import std.array : array;
import std.path : baseName;
return dirEntries(pathname, SpanMode.shallow)
.filter!(a => a.isFile)
.map!((return a) => baseName(a.name))
.array;
}
// Can be safe only with -preview=dip1000
@safe void main(string[] args)
{
import std.stdio : writefln;
string[] files = listdir(args[1]);
writefln("%s", files);
}
dirEntries("/sys/devices/system/node", (enum) std.file.SpanModeDictates directory spanning policy for dirEntries (see below).
Examples
import std.algorithm.comparison : equal;
import std.algorithm.iteration : map;
import std.algorithm.sorting : sort;
import std.array : array;
import std.path : buildPath, relativePath;
auto root = deleteme ~ "root";
scope(exit) root.rmdirRecurse;
root.mkdir;
root.buildPath("animals").mkdir;
root.buildPath("animals", "cat").mkdir;
alias removeRoot = (return scope e) => e.relativePath(root);
assert(root.dirEntries(SpanMode.depth).map!removeRoot.equal(
[buildPath("animals", "cat"), "animals"]));
assert(root.dirEntries(SpanMode.breadth).map!removeRoot.equal(
["animals", buildPath("animals", "cat")]));
root.buildPath("plants").mkdir;
assert(root.dirEntries(SpanMode.shallow).array.sort.map!removeRoot.equal(
["animals", "plants"]));
SpanMode.(enum value) std.file.SpanMode.shallow = 0Only spans one directory.
shallow))
if ((local variable) std.file.DirEntry ee.string std.path.baseName!(immutable(char))(return scope string path) pure nothrow @nogc @safeNote
This function only strips away the specified suffix, which
doesn't necessarily have to represent an extension.
To remove the extension from a path, regardless of what the extension
is, use stripExtension.
To obtain the filename without leading directories and without
an extension, combine the functions like this:
assert(baseName(stripExtension("dir/file.ext")) == "file");
baseName.bool std.algorithm.searching.startsWith!("a == b", string, string)(string doesThisStart, string withThis) pure nothrow @nogc @safeChecks whether the given
input range starts with (one
of) the given needle(s) or, if no needles are given,
if its front element fulfils predicate pred.
For more information about pred see find.
startsWith("node"))
(local variable) int cc++;
if ((local variable) int cc > 0)
(local variable) int nodesOnlinenodesOnline = (local variable) int cc;
}
}
// Pin to CPU 0 for a stable per-thread IBS context and home node.
ulong[16] (local variable) ulong[16] cpuMaskcpuMask;
(local variable) ulong[16] cpuMaskcpuMask[0] = 1;
int cpu_pmu_mem_latency_numa.sched_setaffinity(int pid, ulong cpusetsize, const(void)* mask) nothrow @nogcsched_setaffinity(0, (local variable) ulong[16] cpuMaskcpuMask.(constant) ulong ulong[16].sizeof = 128LUsizeof, (local variable) ulong[16] cpuMaskcpuMask.(constant) ulong* ulong[16].ptr = &cpuMaskptr);
// ---- workload buffer + its home node --------------------------------
enum (constant) int cpu_pmu_mem_latency_numa.run.bufBytes = 67108864bufBytes = 64 * 1024 * 1024; // > per-CCX L3, so misses reach DRAM
void* (local variable) void* rawraw = mmap(null, (constant) int cpu_pmu_mem_latency_numa.run.bufBytes = 67108864bufBytes, (constant) int core.sys.posix.sys.mman.PROT_READ = 1PROT_READ | (constant) int core.sys.posix.sys.mman.PROT_WRITE = 2PROT_WRITE, (constant) int core.sys.posix.sys.mman.MAP_PRIVATE = 2MAP_PRIVATE | (constant) int cpu_pmu_mem_latency_numa.MAP_ANON = 32MAP_ANON, -1, 0);
if ((local variable) void* rawraw == (constant) void* core.sys.posix.sys.mman.MAP_FAILED = cast(void*)cast(size_t)18446744073709551615LUMAP_FAILED)
{
void std.stdio.writefln!(char, int)(in char[] fmt, int __param_1) @safeEquivalent to writef(fmt, args, '\n').
writefln("SKIP: could not mmap a %d MiB workload buffer", (constant) int cpu_pmu_mem_latency_numa.run.bufBytes = 67108864bufBytes >> 20);
return 0;
}
auto (local variable) ulong[] wordswords = (cast((alias) object.size_t = ulongsize_t*) (local variable) void* rawraw)[0 .. (constant) int cpu_pmu_mem_latency_numa.run.bufBytes = 67108864bufBytes / (ulong) ulongsize_t.(constant) ulong ulong.sizeof = 8LUsizeof];
void cpu_pmu_mem_latency_numa.buildChase(ulong[] words) @safeLay out words as one random permutation cycle of its own indices
(Sattolo's algorithm → a single cycle). Chasing i = words[i] is then a
dependent load per step whose target is unpredictable, so on a working set
wider than L3 nearly every step misses to DRAM — the classic pointer-chase
memory-latency pattern. Also faults every page in before classification.
buildChase((local variable) ulong[] wordswords); // also faults every page in before classifying
const (local variable) const(int) homeNodehomeNode = int cpu_pmu_mem_latency_numa.nodeViaGetMempolicy(void* addr) nothrow @nogc @trustedNode of the page containing addr, or a negative -errno, via
get_mempolicy(MPOL_F_NODE | MPOL_F_ADDR). numaif.h:
long get_mempolicy(int *mode, ulong nmask, ulong maxnode,
void addr, ulong flags);
with these flags, mode receives the node number.
nodeViaGetMempolicy((local variable) void* rawraw);
// ---- open the sampling event ----------------------------------------
// Try richest sample set first (with PHYS_ADDR), then drop PHYS_ADDR;
// and prefer kernel exclusion, then fall back to unfiltered — so a
// stricter host still yields a working event.
//
// IBS filtering surprise: this Zen 4 lacks IBS_CAPS_BIT63_FILTER, so a
// bare `exclude_kernel`/`exclude_hv` is EINVAL (perf_ibs_init,
// arch/x86/events/amd/ibs.c). Kernel/user filtering must instead engage
// the software filter — the `swfilt` bit (config2:0, IBS_SW_FILTER_MASK).
// `exclude_hv` is never set: IBS rejects it outright.
enum (constant) ulong cpu_pmu_mem_latency_numa.run.swfilt = 1LUswfilt = 1UL; // config2:0
enum (constant) core.sys.linux.perf_event.perf_event_sample_format cpu_pmu_mem_latency_numa.run.baseType = cast(perf_event_sample_format)49161ubaseType = (constant) core.sys.linux.perf_event.perf_event_sample_format cpu_pmu_mem_latency_numa.PERF_SAMPLE_IP = perf_event_sample_format.PERF_SAMPLE_IPPERF_SAMPLE_IP | (constant) core.sys.linux.perf_event.perf_event_sample_format cpu_pmu_mem_latency_numa.PERF_SAMPLE_ADDR = perf_event_sample_format.PERF_SAMPLE_ADDRPERF_SAMPLE_ADDR | (constant) core.sys.linux.perf_event.perf_event_sample_format cpu_pmu_mem_latency_numa.PERF_SAMPLE_DATA_SRC = perf_event_sample_format.PERF_SAMPLE_DATA_SRCPERF_SAMPLE_DATA_SRC | (constant) core.sys.linux.perf_event.perf_event_sample_format cpu_pmu_mem_latency_numa.PERF_SAMPLE_WEIGHT = perf_event_sample_format.PERF_SAMPLE_WEIGHTPERF_SAMPLE_WEIGHT;
int (local variable) int fdfd = -1;
ulong (local variable) ulong sampleTypesampleType;
foreach ((parameter) bool withPhyswithPhys; [true, false])
foreach ((local variable) int exclKernelexclKernel; [1, 0])
{
(struct) core.sys.linux.perf_event.perf_event_attrHardware event_id to monitor via a performance monitoring event:
@sample_max_stack: Max number of frame pointers in a callchain,
should be < /proc/sys/kernel/perf_event_max_stack
perf_event_attr (local variable) core.sys.linux.perf_event.perf_event_attr attrattr;
(local variable) core.sys.linux.perf_event.perf_event_attr attrattr.(field) uint core.sys.linux.perf_event.perf_event_attr.sizeSize of the attr structure, for fwd/bwd compat.
size = (struct) core.sys.linux.perf_event.perf_event_attrHardware event_id to monitor via a performance monitoring event:
@sample_max_stack: Max number of frame pointers in a callchain,
should be < /proc/sys/kernel/perf_event_max_stack
perf_event_attr.(constant) ulong core.sys.linux.perf_event.perf_event_attr.sizeof = 112LUsizeof;
(local variable) core.sys.linux.perf_event.perf_event_attr attrattr.(field) uint core.sys.linux.perf_event.perf_event_attr.typeMajor type: hardware/software/tracepoint/etc.
type = cast(uint) (local variable) long pmuTypepmuType;
(local variable) core.sys.linux.perf_event.perf_event_attr attrattr.(field) ulong core.sys.linux.perf_event.perf_event_attr.configType specific configuration information.
config = 0; // ibs_op: cnt_ctl=0 (cycles), no ldlat filter
(local variable) core.sys.linux.perf_event.perf_event_attr attrattr.(field) ulong core.sys.linux.perf_event.perf_event_attr.config2extension of config1
config2 = ((local variable) bool viaIbsviaIbs && (local variable) int exclKernelexclKernel) ? (constant) ulong cpu_pmu_mem_latency_numa.run.swfilt = 1LUswfilt : 0;
(local variable) core.sys.linux.perf_event.perf_event_attr attrattr.(field) ulong core.sys.linux.perf_event.perf_event_attr.sample_periodsample_period = 20_000; // ibs_op min_period is 0x90
(local variable) ulong sampleTypesampleType = (constant) core.sys.linux.perf_event.perf_event_sample_format cpu_pmu_mem_latency_numa.run.baseType = cast(perf_event_sample_format)49161ubaseType | ((local variable) bool withPhyswithPhys ? (constant) core.sys.linux.perf_event.perf_event_sample_format cpu_pmu_mem_latency_numa.PERF_SAMPLE_PHYS_ADDR = perf_event_sample_format.PERF_SAMPLE_PHYS_ADDRPERF_SAMPLE_PHYS_ADDR : 0);
(local variable) core.sys.linux.perf_event.perf_event_attr attrattr.(field) ulong core.sys.linux.perf_event.perf_event_attr.sample_typesample_type = (local variable) ulong sampleTypesampleType;
(local variable) core.sys.linux.perf_event.perf_event_attr attrattr.void core.sys.linux.perf_event.perf_event_attr.disabled(ulong v) pure nothrow @nogc @property @safedisabled = 1;
(local variable) core.sys.linux.perf_event.perf_event_attr attrattr.void core.sys.linux.perf_event.perf_event_attr.exclude_kernel(ulong v) pure nothrow @nogc @property @safeexclude_kernel = (local variable) int exclKernelexclKernel;
if (!(local variable) bool viaIbsviaIbs)
(local variable) core.sys.linux.perf_event.perf_event_attr attrattr.void core.sys.linux.perf_event.perf_event_attr.precise_ip(ulong v) pure nothrow @nogc @property @safeprecise_ip = (local variable) int maxPrecisemaxPrecise; // cpu-PMU (Intel) fallback path
(local variable) int fdfd = cast(int) long core.sys.linux.perf_event.perf_event_open(core.sys.linux.perf_event.perf_event_attr* hw_event, int pid, int cpu, int group_fd, ulong flags) nothrow @nogcperf_event_open(&(local variable) core.sys.linux.perf_event.perf_event_attr attrattr, 0, -1, -1, 0);
if ((local variable) int fdfd >= 0)
goto opened;
}
opened:
if ((local variable) int fdfd < 0)
{
void std.stdio.writefln!(char, string)(in char[] fmt, string __param_1) @safeEquivalent to writef(fmt, args, '\n').
writefln("SKIP: perf_event_open on %s failed — perf_event_paranoid, "
~ "seccomp, or unsupported sample type", (local variable) bool viaIbsviaIbs ? "ibs_op" : "cpu");
int core.sys.posix.sys.mman.munmap(void*, ulong) nothrow @nogcmunmap((local variable) void* rawraw, (constant) int cpu_pmu_mem_latency_numa.run.bufBytes = 67108864bufBytes);
return 0;
}
// ---- mmap the sample ring -------------------------------------------
enum (constant) int cpu_pmu_mem_latency_numa.run.dataPages = 128dataPages = 128; // power of two
const (local variable) const(ulong) mmapBytesmmapBytes = (1 + (constant) int cpu_pmu_mem_latency_numa.run.dataPages = 128dataPages) * (local variable) const(ulong) pageSizepageSize;
void* (local variable) void* ringring = mmap(null, (local variable) const(ulong) mmapBytesmmapBytes, (constant) int core.sys.posix.sys.mman.PROT_READ = 1PROT_READ | (constant) int core.sys.posix.sys.mman.PROT_WRITE = 2PROT_WRITE, (constant) int core.sys.posix.sys.mman.MAP_SHARED = 1MAP_SHARED, (local variable) int fdfd, 0);
if ((local variable) void* ringring == (constant) void* core.sys.posix.sys.mman.MAP_FAILED = cast(void*)cast(size_t)18446744073709551615LUMAP_FAILED)
{
void std.stdio.writefln!char(in char[] fmt) @safeEquivalent to writef(fmt, args, '\n').
writefln("SKIP: could not mmap the perf ring buffer");
int core.sys.posix.unistd.close(int) nothrow @nogc @trustedclose((local variable) int fdfd);
int core.sys.posix.sys.mman.munmap(void*, ulong) nothrow @nogcmunmap((local variable) void* rawraw, (constant) int cpu_pmu_mem_latency_numa.run.bufBytes = 67108864bufBytes);
return 0;
}
auto (local variable) core.sys.linux.perf_event.perf_event_mmap_page* metameta = cast((struct) core.sys.linux.perf_event.perf_event_mmap_pageStructure of the page that can be mapped via mmap
perf_event_mmap_page*) (local variable) void* ringring;
const (local variable) const(ubyte*) dataStartdataStart = cast(const(ubyte)*) (local variable) void* ringring + (local variable) core.sys.linux.perf_event.perf_event_mmap_page* metameta.(field) ulong core.sys.linux.perf_event.perf_event_mmap_page.data_offsetwhere the buffer starts
data_offset;
const (local variable) const(ulong) dataSizedataSize = (local variable) core.sys.linux.perf_event.perf_event_mmap_page* metameta.(field) ulong core.sys.linux.perf_event.perf_event_mmap_page.data_sizedata buffer size
data_size;
// ---- sample: enable, stride, drain ----------------------------------
import (package) corecore.(package) core.syssys.(package) core.sys.posixposix.(package) core.sys.posix.syssys.(module) core.sys.posix.sys.ioctlD header file for POSIX.
ioctl : (alias) ioctl = int core.sys.posix.sys.ioctl.ioctl(int __fd, ulong __request, ...) nothrow @nogcioctl;
import (package) corecore.(package) core.stdcstdc.(module) core.stdc.configD compatible types that correspond to various basic types in associated
C and C++ compilers.
Source
core/stdc/config.d
config : c_ulong;
(struct) cpu_pmu_mem_latency_numa.SampleOne decoded PERF_RECORD_SAMPLE (only the fields this probe requests).
Sample[] (local variable) cpu_pmu_mem_latency_numa.Sample[] samplessamples;
ulong (local variable) ulong totalRecordstotalRecords, (local variable) ulong lostRecordslostRecords;
void void cpu_pmu_mem_latency_numa.run.drain() @trusteddrain() @trusted
{
auto (local variable) ulong headhead = ulong core.atomic.atomicLoad!(MemoryOrder.acq, ulong)(ref return scope shared(const(ulong)) val) pure nothrow @nogc @trustedLoads 'val' from memory and returns it. The memory barrier specified
by 'ms' is applied to the operation, which is fully sequenced by
default. Valid memory orders are MemoryOrder.raw, MemoryOrder.acq,
and MemoryOrder.seq.
atomicLoad!((enum) core.atomic.MemoryOrderSpecifies the memory ordering semantics of an atomic operation.
MemoryOrder.(enum value) core.atomic.MemoryOrder.acq = 2Hoist-load + hoist-store barrier.
Corresponds to LLVM AtomicOrdering.Acquire
and C++11/C11 memory_order_acquire.
acq)(*cast(shared(ulong)*)&(local variable) core.sys.linux.perf_event.perf_event_mmap_page* metameta.(field) ulong core.sys.linux.perf_event.perf_event_mmap_page.data_headControl data for the mmap() data buffer.
User-space reading the @data_head value should issue an smp_rmb(),
after reading this value.
When the mapping is PROT_WRITE the @data_tail value should be
written by userspace to reflect the last read data, after issueing
an smp_mb() to separate the data read from the ->data_tail store.
In this case the kernel will not over-write unread data.
See perf_output_put_handle() for the data ordering.
data_{offset,size} indicate the location and size of the perf record
buffer within the mmapped area.
head in the data section
data_head);
auto (local variable) ulong tailtail = (local variable) core.sys.linux.perf_event.perf_event_mmap_page* metameta.(field) ulong core.sys.linux.perf_event.perf_event_mmap_page.data_tailuser-space written tail
data_tail;
while ((local variable) ulong tailtail < (local variable) ulong headhead)
{
(struct) core.sys.linux.perf_event.perf_event_headerperf_event_header (local variable) core.sys.linux.perf_event.perf_event_header hdrhdr;
void cpu_pmu_mem_latency_numa.ringCopy(void* dst, const(ubyte)* dataStart, ulong dataSize, ulong logicalTail, ulong n) @systemCopies n bytes out of the perf data area at logical offset logicalTail
(which may straddle the ring's wrap point) into dst.
ringCopy(&(local variable) core.sys.linux.perf_event.perf_event_header hdrhdr, (local variable) const(ubyte*) dataStartdataStart, (local variable) const(ulong) dataSizedataSize, (local variable) ulong tailtail, (local variable) core.sys.linux.perf_event.perf_event_header hdrhdr.(constant) ulong core.sys.linux.perf_event.perf_event_header.sizeof = 8LUsizeof);
if ((local variable) core.sys.linux.perf_event.perf_event_header hdrhdr.(field) uint core.sys.linux.perf_event.perf_event_header.typetype == (enum) core.sys.linux.perf_event.perf_event_typeperf_event_type.(enum value) core.sys.linux.perf_event.perf_event_type.PERF_RECORD_SAMPLE = 9struct {
struct perf_event_header header;
#
# Note that PERF_SAMPLE_IDENTIFIER duplicates PERF_SAMPLE_ID.
# The advantage of PERF_SAMPLE_IDENTIFIER is that its position
# is fixed relative to header.
#
{ u64 id; } && PERF_SAMPLE_IDENTIFIER
{ u64 ip; } && PERF_SAMPLE_IP
{ u32 pid, tid; } && PERF_SAMPLE_TID
{ u64 time; } && PERF_SAMPLE_TIME
{ u64 addr; } && PERF_SAMPLE_ADDR
{ u64 id; } && PERF_SAMPLE_ID
{ u64 stream_id;} && PERF_SAMPLE_STREAM_ID
{ u32 cpu, res; } && PERF_SAMPLE_CPU
{ u64 period; } && PERF_SAMPLE_PERIOD
{ struct read_format values; } && PERF_SAMPLE_READ
{ u64 nr,
u64 ips[nr]; } && PERF_SAMPLE_CALLCHAIN
#
# The RAW record below is opaque data wrt the ABI
#
# That is, the ABI doesn't make any promises wrt to
# the stability of its content, it may vary depending
# on event, hardware, kernel version and phase of
# the moon.
#
# In other words, PERF_SAMPLE_RAW contents are not an ABI.
#
{ u32 size;
char data[size];}&& PERF_SAMPLE_RAW
{ u64 nr;
{ u64 from, to, flags } lbr[nr];} && PERF_SAMPLE_BRANCH_STACK
{ u64 abi; # enum perf_sample_regs_abi
u64 regs[weight(mask)]; } && PERF_SAMPLE_REGS_USER
{ u64 size;
char data[size];
u64 dyn_size; } && PERF_SAMPLE_STACK_USER
{ u64 weight; } && PERF_SAMPLE_WEIGHT
{ u64 data_src; } && PERF_SAMPLE_DATA_SRC
{ u64 transaction; } && PERF_SAMPLE_TRANSACTION
{ u64 abi; # enum perf_sample_regs_abi
u64 regs[weight(mask)]; } && PERF_SAMPLE_REGS_INTR
{ u64 phys_addr;} && PERF_SAMPLE_PHYS_ADDR
};
PERF_RECORD_SAMPLE)
{
ubyte[256] (local variable) ubyte[256] recrec;
void cpu_pmu_mem_latency_numa.ringCopy(void* dst, const(ubyte)* dataStart, ulong dataSize, ulong logicalTail, ulong n) @systemCopies n bytes out of the perf data area at logical offset logicalTail
(which may straddle the ring's wrap point) into dst.
ringCopy((local variable) ubyte[256] recrec.(constant) ubyte* ubyte[256].ptr = &recptr, (local variable) const(ubyte*) dataStartdataStart, (local variable) const(ulong) dataSizedataSize, (local variable) ulong tailtail, ushort std.algorithm.comparison.min!(ushort, ulong)(ushort __param_0, ulong __param_1) pure nothrow @nogc @safeIterates the passed arguments and returns the minimum value.
min((local variable) core.sys.linux.perf_event.perf_event_header hdrhdr.(field) ushort core.sys.linux.perf_event.perf_event_header.sizesize, (local variable) ubyte[256] recrec.(constant) ulong ubyte[256].length = 256LUlength));
(alias) object.size_t = ulongsize_t (local variable) ulong curcur = (local variable) core.sys.linux.perf_event.perf_event_header hdrhdr.(constant) ulong core.sys.linux.perf_event.perf_event_header.sizeof = 8LUsizeof;
ulong ulong cpu_pmu_mem_latency_numa.run.drain.take() pure nothrow @nogc @systemtake() { const (local variable) const(ulong) vv = *cast(ulong*)((local variable) ubyte[256] recrec.(constant) ubyte* ubyte[256].ptr = &recptr + (local variable) ulong curcur); (local variable) ulong curcur += 8; return (local variable) const(ulong) vv; }
(struct) cpu_pmu_mem_latency_numa.SampleOne decoded PERF_RECORD_SAMPLE (only the fields this probe requests).
Sample (local variable) cpu_pmu_mem_latency_numa.Sample ss;
if ((local variable) ulong sampleTypesampleType & (constant) core.sys.linux.perf_event.perf_event_sample_format cpu_pmu_mem_latency_numa.PERF_SAMPLE_IP = perf_event_sample_format.PERF_SAMPLE_IPPERF_SAMPLE_IP) (local variable) cpu_pmu_mem_latency_numa.Sample ss.(field) ulong cpu_pmu_mem_latency_numa.Sample.ipip = ulong cpu_pmu_mem_latency_numa.run.drain.take() pure nothrow @nogc @systemtake();
if ((local variable) ulong sampleTypesampleType & (constant) core.sys.linux.perf_event.perf_event_sample_format cpu_pmu_mem_latency_numa.PERF_SAMPLE_ADDR = perf_event_sample_format.PERF_SAMPLE_ADDRPERF_SAMPLE_ADDR) (local variable) cpu_pmu_mem_latency_numa.Sample ss.(field) ulong cpu_pmu_mem_latency_numa.Sample.addraddr = ulong cpu_pmu_mem_latency_numa.run.drain.take() pure nothrow @nogc @systemtake();
if ((local variable) ulong sampleTypesampleType & (constant) core.sys.linux.perf_event.perf_event_sample_format cpu_pmu_mem_latency_numa.PERF_SAMPLE_WEIGHT = perf_event_sample_format.PERF_SAMPLE_WEIGHTPERF_SAMPLE_WEIGHT) (local variable) cpu_pmu_mem_latency_numa.Sample ss.(field) ulong cpu_pmu_mem_latency_numa.Sample.weightweight = ulong cpu_pmu_mem_latency_numa.run.drain.take() pure nothrow @nogc @systemtake();
if ((local variable) ulong sampleTypesampleType & (constant) core.sys.linux.perf_event.perf_event_sample_format cpu_pmu_mem_latency_numa.PERF_SAMPLE_DATA_SRC = perf_event_sample_format.PERF_SAMPLE_DATA_SRCPERF_SAMPLE_DATA_SRC) (local variable) cpu_pmu_mem_latency_numa.Sample ss.(field) ulong cpu_pmu_mem_latency_numa.Sample.dataSrcdataSrc = ulong cpu_pmu_mem_latency_numa.run.drain.take() pure nothrow @nogc @systemtake();
if ((local variable) ulong sampleTypesampleType & (constant) core.sys.linux.perf_event.perf_event_sample_format cpu_pmu_mem_latency_numa.PERF_SAMPLE_PHYS_ADDR = perf_event_sample_format.PERF_SAMPLE_PHYS_ADDRPERF_SAMPLE_PHYS_ADDR) (local variable) cpu_pmu_mem_latency_numa.Sample ss.(field) ulong cpu_pmu_mem_latency_numa.Sample.physAddrphysAddr = ulong cpu_pmu_mem_latency_numa.run.drain.take() pure nothrow @nogc @systemtake();
(local variable) cpu_pmu_mem_latency_numa.Sample[] samplessamples ~= (local variable) cpu_pmu_mem_latency_numa.Sample ss;
(local variable) ulong totalRecordstotalRecords++;
}
else if ((local variable) core.sys.linux.perf_event.perf_event_header hdrhdr.(field) uint core.sys.linux.perf_event.perf_event_header.typetype == (enum) core.sys.linux.perf_event.perf_event_typeperf_event_type.(enum value) core.sys.linux.perf_event.perf_event_type.PERF_RECORD_LOST = 2struct {
struct perf_event_header header;
u64 id;
u64 lost;
struct sample_id sample_id;
};
PERF_RECORD_LOST)
(local variable) ulong lostRecordslostRecords++;
(local variable) ulong tailtail += (local variable) core.sys.linux.perf_event.perf_event_header hdrhdr.(field) ushort core.sys.linux.perf_event.perf_event_header.sizesize;
}
void core.atomic.atomicStore!(MemoryOrder.rel, ulong, ulong)(ref shared(ulong) val, ulong newval) pure nothrow @nogc @trustedWrites 'newval' into 'val'. The memory barrier specified by 'ms' is
applied to the operation, which is fully sequenced by default.
Valid memory orders are MemoryOrder.raw, MemoryOrder.rel, and
MemoryOrder.seq.
atomicStore!((enum) core.atomic.MemoryOrderSpecifies the memory ordering semantics of an atomic operation.
MemoryOrder.(enum value) core.atomic.MemoryOrder.rel = 3Sink-load + sink-store barrier.
Corresponds to LLVM AtomicOrdering.Release
and C++11/C11 memory_order_release.
rel)(*cast(shared(ulong)*)&(local variable) core.sys.linux.perf_event.perf_event_mmap_page* metameta.(field) ulong core.sys.linux.perf_event.perf_event_mmap_page.data_tailuser-space written tail
data_tail, (local variable) ulong headhead);
}
int core.sys.posix.sys.ioctl.ioctl(int __fd, ulong __request, ...) nothrow @nogcioctl((local variable) int fdfd, cast(c_ulong) (constant) int core.sys.linux.perf_event.PERF_EVENT_IOC_RESET = 9219PERF_EVENT_IOC_RESET, 0);
int core.sys.posix.sys.ioctl.ioctl(int __fd, ulong __request, ...) nothrow @nogcioctl((local variable) int fdfd, cast(c_ulong) (constant) int core.sys.linux.perf_event.PERF_EVENT_IOC_ENABLE = 9216Ioctls that can be done on a perf event fd:
PERF_EVENT_IOC_ENABLE, 0);
(alias) object.size_t = ulongsize_t (local variable) ulong pospos;
foreach ((local variable) int passpass; 0 .. 200)
{
(local variable) ulong pospos = ulong cpu_pmu_mem_latency_numa.chase(const(ulong[]) words, ulong start, ulong steps) @safesteps dependent chases from start; returns the landing index (folded
into a _gshared sink by the caller so the loads survive DCE).
chase((local variable) ulong[] wordswords, (local variable) ulong pospos, 100_000);
(__gshared global) ulong cpu_pmu_mem_latency_numa.sinksink += (local variable) ulong pospos;
void cpu_pmu_mem_latency_numa.run.drain() @trusteddrain();
if ((local variable) cpu_pmu_mem_latency_numa.Sample[] samplessamples.(field) ulong cpu_pmu_mem_latency_numa.Sample[].lengthlength >= 4000)
break;
}
int core.sys.posix.sys.ioctl.ioctl(int __fd, ulong __request, ...) nothrow @nogcioctl((local variable) int fdfd, cast(c_ulong) (constant) int core.sys.linux.perf_event.PERF_EVENT_IOC_DISABLE = 9217PERF_EVENT_IOC_DISABLE, 0);
void cpu_pmu_mem_latency_numa.run.drain() @trusteddrain();
int core.sys.posix.sys.mman.munmap(void*, ulong) nothrow @nogcmunmap((local variable) void* ringring, (local variable) const(ulong) mmapBytesmmapBytes);
int core.sys.posix.unistd.close(int) nothrow @nogc @trustedclose((local variable) int fdfd);
// ---- report ---------------------------------------------------------
void std.stdio.writefln!(char, string)(in char[] fmt, string __param_1) @safeEquivalent to writef(fmt, args, '\n').
writefln("== precise memory-access sampling: %s ==", (local variable) bool viaIbsviaIbs
? ((local variable) const(bool) zen4zen4 ? "AMD IBS (ibs_op, zen4_ibs_extensions)" : "AMD IBS (ibs_op)")
: "cpu PMU precise_ip (Intel PEBS path — UNVERIFIED on this host)");
void std.stdio.writefln!(char, long, string)(in char[] fmt, long __param_1, string __param_2) @safeEquivalent to writef(fmt, args, '\n').
writefln(" PMU type=%d period=20000 sample_type=IP|ADDR|DATA_SRC|WEIGHT%s",
(local variable) long pmuTypepmuType, ((local variable) ulong sampleTypesampleType & (constant) core.sys.linux.perf_event.perf_event_sample_format cpu_pmu_mem_latency_numa.PERF_SAMPLE_PHYS_ADDR = perf_event_sample_format.PERF_SAMPLE_PHYS_ADDRPERF_SAMPLE_PHYS_ADDR) ? "|PHYS_ADDR" : "");
void std.stdio.writefln!(char, int, const(int), int)(in char[] fmt, int __param_1, const(int) __param_2, int __param_3) @safeEquivalent to writef(fmt, args, '\n').
writefln(" workload=%d MiB home node(get_mempolicy)=%d NUMA nodes online=%d",
(constant) int cpu_pmu_mem_latency_numa.run.bufBytes = 67108864bufBytes >> 20, (local variable) const(int) homeNodehomeNode, (local variable) int nodesOnlinenodesOnline);
if ((local variable) int nodesOnlinenodesOnline <= 1)
void std.stdio.writeln!string(string __param_0) @safeEquivalent to write(args, '\n'). Calling writeln without
arguments is valid and just prints a newline to the standard
output.
Example
Reads stdin and writes it to stdout with an argument
counter.
import std.stdio;
void main()
{
string line;
for (size_t count = 0; (line = readln) !is null; count++)
{
writeln("Input ", count, ": ", line);
}
}
writeln(" (single-node host: node round-trip is demonstrated; cross-node "
~ "classification is not — that needs a multi-socket box)");
// Keep only samples with a resolved data address (IBS sets ADDR only when
// DcLinAddrValid). Classify each such address to a node, both ways.
struct (struct) cpu_pmu_mem_latency_numa.run.RowRow { (struct) cpu_pmu_mem_latency_numa.SampleOne decoded PERF_RECORD_SAMPLE (only the fields this probe requests).
Sample (field) cpu_pmu_mem_latency_numa.Sample cpu_pmu_mem_latency_numa.run.Row.ss; int (field) int cpu_pmu_mem_latency_numa.run.Row.nGmpnGmp, (field) int cpu_pmu_mem_latency_numa.run.Row.nMvpnMvp; }
(struct) cpu_pmu_mem_latency_numa.run.RowRow[] (local variable) cpu_pmu_mem_latency_numa.run.Row[] rowsrows;
foreach ((parameter) cpu_pmu_mem_latency_numa.Sample ss; (local variable) cpu_pmu_mem_latency_numa.Sample[] samplessamples)
if ((local variable) cpu_pmu_mem_latency_numa.Sample ss.(field) ulong cpu_pmu_mem_latency_numa.Sample.addraddr != 0 && string cpu_pmu_mem_latency_numa.memOpStr(ulong ds) pure nothrow @nogc @safememOpStr((local variable) cpu_pmu_mem_latency_numa.Sample ss.(field) ulong cpu_pmu_mem_latency_numa.Sample.dataSrcdataSrc) != "N/A")
(local variable) cpu_pmu_mem_latency_numa.run.Row[] rowsrows ~= (struct) cpu_pmu_mem_latency_numa.run.RowRow((local variable) cpu_pmu_mem_latency_numa.Sample ss, int cpu_pmu_mem_latency_numa.nodeViaGetMempolicy(void* addr) nothrow @nogc @trustedNode of the page containing addr, or a negative -errno, via
get_mempolicy(MPOL_F_NODE | MPOL_F_ADDR). numaif.h:
long get_mempolicy(int *mode, ulong nmask, ulong maxnode,
void addr, ulong flags);
with these flags, mode receives the node number.
nodeViaGetMempolicy(cast(void*) (local variable) cpu_pmu_mem_latency_numa.Sample ss.(field) ulong cpu_pmu_mem_latency_numa.Sample.addraddr),
int cpu_pmu_mem_latency_numa.nodeViaMovePages(void* addr, ulong pageSize) nothrow @nogc @trustedNode of the page containing addr via move_pages() query mode (nodes ==
NULL). numaif.h:
long move_pages(int pid, ulong count, void **pages,
const int nodes, int status, int flags);
status[0] receives the node number (or a negative -errno).
nodeViaMovePages(cast(void*) (local variable) cpu_pmu_mem_latency_numa.Sample ss.(field) ulong cpu_pmu_mem_latency_numa.Sample.addraddr, (local variable) const(ulong) pageSizepageSize));
if ((local variable) cpu_pmu_mem_latency_numa.run.Row[] rowsrows.(field) ulong cpu_pmu_mem_latency_numa.run.Row[].lengthlength == 0)
{
void std.stdio.writefln!(char, ulong, ulong)(in char[] fmt, ulong __param_1, ulong __param_2) @safeEquivalent to writef(fmt, args, '\n').
writefln(" collected %d raw samples, %d with a usable data address — "
~ "reduced output (no per-address rows to show)", (local variable) cpu_pmu_mem_latency_numa.Sample[] samplessamples.(field) ulong cpu_pmu_mem_latency_numa.Sample[].lengthlength, (local variable) cpu_pmu_mem_latency_numa.run.Row[] rowsrows.(field) ulong cpu_pmu_mem_latency_numa.run.Row[].lengthlength);
int core.sys.posix.sys.mman.munmap(void*, ulong) nothrow @nogcmunmap((local variable) void* rawraw, (constant) int cpu_pmu_mem_latency_numa.run.bufBytes = 67108864bufBytes);
return 0;
}
// Lead with samples that missed the L1 — those exercise the whole
// data-source/latency path; pad with L1 hits if there are few.
(struct) cpu_pmu_mem_latency_numa.run.RowRow[] (local variable) cpu_pmu_mem_latency_numa.run.Row[] showshow;
foreach ((parameter) cpu_pmu_mem_latency_numa.run.Row rr; (local variable) cpu_pmu_mem_latency_numa.run.Row[] rowsrows)
if (string cpu_pmu_mem_latency_numa.memLvlStr(ulong ds) nothrow @safememLvlStr((local variable) cpu_pmu_mem_latency_numa.run.Row rr.(field) cpu_pmu_mem_latency_numa.Sample cpu_pmu_mem_latency_numa.run.Row.ss.(field) ulong cpu_pmu_mem_latency_numa.Sample.dataSrcdataSrc) != "L1 hit" && (local variable) cpu_pmu_mem_latency_numa.run.Row[] showshow.(field) ulong cpu_pmu_mem_latency_numa.run.Row[].lengthlength < 8)
(local variable) cpu_pmu_mem_latency_numa.run.Row[] showshow ~= (local variable) cpu_pmu_mem_latency_numa.run.Row rr;
foreach ((parameter) cpu_pmu_mem_latency_numa.run.Row rr; (local variable) cpu_pmu_mem_latency_numa.run.Row[] rowsrows)
if ((local variable) cpu_pmu_mem_latency_numa.run.Row[] showshow.(field) ulong cpu_pmu_mem_latency_numa.run.Row[].lengthlength < 8)
(local variable) cpu_pmu_mem_latency_numa.run.Row[] showshow ~= (local variable) cpu_pmu_mem_latency_numa.run.Row rr;
void std.stdio.writefln!(char, ulong, ulong)(in char[] fmt, ulong __param_1, ulong __param_2) @safeEquivalent to writef(fmt, args, '\n').
writefln("\n sampled data accesses (%d of %d; cache-miss samples first):",
(local variable) cpu_pmu_mem_latency_numa.run.Row[] showshow.(field) ulong cpu_pmu_mem_latency_numa.run.Row[].lengthlength, (local variable) cpu_pmu_mem_latency_numa.run.Row[] rowsrows.(field) ulong cpu_pmu_mem_latency_numa.run.Row[].lengthlength);
void std.stdio.writeln!string(string __param_0) @safeEquivalent to write(args, '\n'). Calling writeln without
arguments is valid and just prints a newline to the standard
output.
Example
Reads stdin and writes it to stdout with an argument
counter.
import std.stdio;
void main()
{
string line;
for (size_t count = 0; (line = readln) !is null; count++)
{
writeln("Input ", count, ": ", line);
}
}
writeln(" ip addr op level "
~ "snoop tlb lat node[gmp/mvp]");
foreach ((parameter) cpu_pmu_mem_latency_numa.run.Row rr; (local variable) cpu_pmu_mem_latency_numa.run.Row[] showshow)
void std.stdio.writefln!(char, ulong, ulong, string, string, string, string, ulong, int, int)(in char[] fmt, ulong __param_1, ulong __param_2, string __param_3, string __param_4, string __param_5, string __param_6, ulong __param_7, int __param_8, int __param_9) @safeEquivalent to writef(fmt, args, '\n').
writefln(" 0x%016x 0x%016x %-6s %-18s %-6s %-8s %4d %d/%d",
(local variable) cpu_pmu_mem_latency_numa.run.Row rr.(field) cpu_pmu_mem_latency_numa.Sample cpu_pmu_mem_latency_numa.run.Row.ss.(field) ulong cpu_pmu_mem_latency_numa.Sample.ipip, (local variable) cpu_pmu_mem_latency_numa.run.Row rr.(field) cpu_pmu_mem_latency_numa.Sample cpu_pmu_mem_latency_numa.run.Row.ss.(field) ulong cpu_pmu_mem_latency_numa.Sample.addraddr, string cpu_pmu_mem_latency_numa.memOpStr(ulong ds) pure nothrow @nogc @safememOpStr((local variable) cpu_pmu_mem_latency_numa.run.Row rr.(field) cpu_pmu_mem_latency_numa.Sample cpu_pmu_mem_latency_numa.run.Row.ss.(field) ulong cpu_pmu_mem_latency_numa.Sample.dataSrcdataSrc), string cpu_pmu_mem_latency_numa.memLvlStr(ulong ds) nothrow @safememLvlStr((local variable) cpu_pmu_mem_latency_numa.run.Row rr.(field) cpu_pmu_mem_latency_numa.Sample cpu_pmu_mem_latency_numa.run.Row.ss.(field) ulong cpu_pmu_mem_latency_numa.Sample.dataSrcdataSrc),
string cpu_pmu_mem_latency_numa.snoopStr(ulong ds) pure nothrow @nogc @safesnoopStr((local variable) cpu_pmu_mem_latency_numa.run.Row rr.(field) cpu_pmu_mem_latency_numa.Sample cpu_pmu_mem_latency_numa.run.Row.ss.(field) ulong cpu_pmu_mem_latency_numa.Sample.dataSrcdataSrc), string cpu_pmu_mem_latency_numa.tlbStr(ulong ds) pure nothrow @safetlbStr((local variable) cpu_pmu_mem_latency_numa.run.Row rr.(field) cpu_pmu_mem_latency_numa.Sample cpu_pmu_mem_latency_numa.run.Row.ss.(field) ulong cpu_pmu_mem_latency_numa.Sample.dataSrcdataSrc), (local variable) cpu_pmu_mem_latency_numa.run.Row rr.(field) cpu_pmu_mem_latency_numa.Sample cpu_pmu_mem_latency_numa.run.Row.ss.(field) ulong cpu_pmu_mem_latency_numa.Sample.weightweight, (local variable) cpu_pmu_mem_latency_numa.run.Row rr.(field) int cpu_pmu_mem_latency_numa.run.Row.nGmpnGmp, (local variable) cpu_pmu_mem_latency_numa.run.Row rr.(field) int cpu_pmu_mem_latency_numa.run.Row.nMvpnMvp);
// Level histogram + node-agreement summary.
ulong[(alias) object.string = stringstring] (local variable) ulong[string] lvlHistlvlHist;
int (local variable) int agreeHomeagreeHome, (local variable) int disagreedisagree, (local variable) int gmpErrgmpErr, (local variable) int mvpErrmvpErr;
ulong[] (local variable) ulong[] latslats;
foreach ((parameter) cpu_pmu_mem_latency_numa.run.Row rr; (local variable) cpu_pmu_mem_latency_numa.run.Row[] rowsrows)
{
ulong* core.internal.newaa._d_aaGetY!(string, ulong, ulong[string], string, ulong, string)(ref scope ulong[string] aa, string key, out bool found) pure nothrow @safeLookup key in aa.
Called only from implementation of (aakey) expressions when value is mutable.
lvlHist[string cpu_pmu_mem_latency_numa.memLvlStr(ulong ds) nothrow @safememLvlStr((local variable) cpu_pmu_mem_latency_numa.run.Row rr.(field) cpu_pmu_mem_latency_numa.Sample cpu_pmu_mem_latency_numa.run.Row.ss.(field) ulong cpu_pmu_mem_latency_numa.Sample.dataSrcdataSrc)]++;
if ((local variable) cpu_pmu_mem_latency_numa.run.Row rr.(field) int cpu_pmu_mem_latency_numa.run.Row.nGmpnGmp < 0) (local variable) int gmpErrgmpErr++;
if ((local variable) cpu_pmu_mem_latency_numa.run.Row rr.(field) int cpu_pmu_mem_latency_numa.run.Row.nMvpnMvp < 0) (local variable) int mvpErrmvpErr++;
if ((local variable) cpu_pmu_mem_latency_numa.run.Row rr.(field) int cpu_pmu_mem_latency_numa.run.Row.nGmpnGmp >= 0 && (local variable) cpu_pmu_mem_latency_numa.run.Row rr.(field) int cpu_pmu_mem_latency_numa.run.Row.nMvpnMvp >= 0)
{
if ((local variable) cpu_pmu_mem_latency_numa.run.Row rr.(field) int cpu_pmu_mem_latency_numa.run.Row.nGmpnGmp == (local variable) const(int) homeNodehomeNode && (local variable) cpu_pmu_mem_latency_numa.run.Row rr.(field) int cpu_pmu_mem_latency_numa.run.Row.nMvpnMvp == (local variable) const(int) homeNodehomeNode) (local variable) int agreeHomeagreeHome++;
else (local variable) int disagreedisagree++;
}
if ((local variable) cpu_pmu_mem_latency_numa.run.Row rr.(field) cpu_pmu_mem_latency_numa.Sample cpu_pmu_mem_latency_numa.run.Row.ss.(field) ulong cpu_pmu_mem_latency_numa.Sample.weightweight > 0) (local variable) ulong[] latslats ~= (local variable) cpu_pmu_mem_latency_numa.run.Row rr.(field) cpu_pmu_mem_latency_numa.Sample cpu_pmu_mem_latency_numa.run.Row.ss.(field) ulong cpu_pmu_mem_latency_numa.Sample.weightweight;
}
void std.stdio.writefln!(char, ulong)(in char[] fmt, ulong __param_1) @safeEquivalent to writef(fmt, args, '\n').
writefln("\n data-source levels across %d resolved samples:", (local variable) cpu_pmu_mem_latency_numa.run.Row[] rowsrows.(field) ulong cpu_pmu_mem_latency_numa.run.Row[].lengthlength);
foreach (int core.internal.newaa._d_aaApply2!(string, ulong, int delegate(ref string, ref ulong) @safe)(inout(ulong[string]) a, int delegate(ref string, ref ulong) @safe dg) @safeforeach opApply over all key/value pairs
Note
emulated by the compiler during CTFE
k, (parameter) ulong vv; (local variable) ulong[string] lvlHistlvlHist)
void std.stdio.writefln!(char, string, ulong)(in char[] fmt, string __param_1, ulong __param_2) @safeEquivalent to writef(fmt, args, '\n').
writefln(" %-24s %d", (local variable) string kk, (local variable) ulong vv);
void std.stdio.writefln!(char, int, const(int), int, int, int)(in char[] fmt, int __param_1, const(int) __param_2, int __param_3, int __param_4, int __param_5) @safeEquivalent to writef(fmt, args, '\n').
writefln(" node classification: %d on home node %d (get_mempolicy == move_pages), "
~ "%d elsewhere; gmp errors=%d, mvp errors=%d",
(local variable) int agreeHomeagreeHome, (local variable) const(int) homeNodehomeNode, (local variable) int disagreedisagree, (local variable) int gmpErrgmpErr, (local variable) int mvpErrmvpErr);
if ((local variable) ulong[] latslats.(field) ulong ulong[].lengthlength)
{
(local variable) ulong[] latslats.std.range.SortedRange!(ulong[], "a < b", SortedRangeOptions.assumeSorted) std.algorithm.sorting.sort!("a < b", SwapStrategy.unstable, ulong[])(ulong[] r) pure nothrow @nogc @safeSorts a random-access range according to the predicate less.
Performs O(r.length * log(r.length)) evaluations of less. If less involves
expensive computations on the sort key, it may be worthwhile to use
schwartzSort instead.
Stable sorting requires hasAssignableElements!Range to be true.
sort returns a SortedRange over the original range,
allowing functions that can take advantage of sorted data to know that the
range is sorted and adjust accordingly. The SortedRange is a
wrapper around the original range, so both it and the original range are sorted.
Other functions can't know that the original range has been sorted, but
they can know that SortedRange has been sorted.
Preconditions
The predicate is expected to satisfy certain rules in order for sort to
behave as expected - otherwise, the program may fail on certain inputs (but not
others) when not compiled in release mode, due to the cursory assumeSorted
check. Specifically, sort expects less(a,b) && less(b,c) to imply
less(a,c) (transitivity), and, conversely, !less(a,b) && !less(b,c) to
imply !less(a,c). Note that the default predicate ("a < b") does not
always satisfy these conditions for floating point types, because the expression
will always be false when either a or b is NaN.
Use cmp instead.
Algorithms
Introsort is used for unstable sorting and
Timsort is used for stable sorting.
Each algorithm has benefits beyond stability. Introsort is generally faster but
Timsort may achieve greater speeds on data with low entropy or if predicate calls
are expensive. Introsort performs no allocations whereas Timsort will perform one
or more allocations per call. Both algorithms have O(n log n) worst-case
time complexity.
Examples
int[] array = [ 1, 2, 3, 4 ];
// sort in descending order
array.sort!("a > b");
assert(array == [ 4, 3, 2, 1 ]);
// sort in ascending order
array.sort();
assert(array == [ 1, 2, 3, 4 ]);
// sort with reusable comparator and chain
alias myComp = (x, y) => x > y;
assert(array.sort!(myComp).release == [ 4, 3, 2, 1 ]);
// Showcase stable sorting
import std.algorithm.mutation : SwapStrategy;
string[] words = [ "aBc", "a", "abc", "b", "ABC", "c" ];
sort!("toUpper(a) < toUpper(b)", SwapStrategy.stable)(words);
assert(words == [ "a", "aBc", "abc", "ABC", "b", "c" ]);
// Sorting floating-point numbers in presence of NaN
double[] numbers = [-0.0, 3.0, -2.0, double.nan, 0.0, -double.nan];
import std.algorithm.comparison : equal;
import std.math.operations : cmp;
import std.math.traits : isIdentical;
sort!((a, b) => cmp(a, b) < 0)(numbers);
double[] sorted = [-double.nan, -2.0, -0.0, 0.0, 3.0, double.nan];
assert(numbers.equal!isIdentical(sorted));
sort();
void std.stdio.writefln!(char, ulong, ulong, ulong, ulong)(in char[] fmt, ulong __param_1, ulong __param_2, ulong __param_3, ulong __param_4) @safeEquivalent to writef(fmt, args, '\n').
writefln(" DC-miss latency (WEIGHT) on %d load samples: min=%d median=%d max=%d cycles",
(local variable) ulong[] latslats.(field) ulong ulong[].lengthlength, (local variable) ulong[] latslats[0], (local variable) ulong[] latslats[$ / 2], (local variable) ulong[] latslats[$ - 1]);
}
else
void std.stdio.writeln!string(string __param_0) @safeEquivalent to write(args, '\n'). Calling writeln without
arguments is valid and just prints a newline to the standard
output.
Example
Reads stdin and writes it to stdout with an argument
counter.
import std.stdio;
void main()
{
string line;
for (size_t count = 0; (line = readln) !is null; count++)
{
writeln("Input ", count, ": ", line);
}
}
writeln(" DC-miss latency (WEIGHT): none — no sampled load missed the data cache");
int core.sys.posix.sys.mman.munmap(void*, ulong) nothrow @nogcmunmap((local variable) void* rawraw, (constant) int cpu_pmu_mem_latency_numa.run.bufBytes = 67108864bufBytes);
return 0;
}
}
int int D main()main()
{
version (linuxlinux)
return int cpu_pmu_mem_latency_numa.run()run();
else
{
import std.stdio : writefln;
writefln("SKIP: perf_event_open / IBS is Linux-only");
return 0;
}
}