From 39addfed12b36d1e009d08beba4cb8d5ef9946d0 Mon Sep 17 00:00:00 2001 From: Valentin Churavy Date: Fri, 11 Nov 2022 21:34:07 -0500 Subject: [PATCH 01/36] WIP: Add support for PCI devices Co-authored-by: Johannes Blaschke --- src/libhwloc.jl | 74 +++++++++++++++++++++++++++++++++++++++---------- 1 file changed, 60 insertions(+), 14 deletions(-) diff --git a/src/libhwloc.jl b/src/libhwloc.jl index dfed3b7..21fd761 100644 --- a/src/libhwloc.jl +++ b/src/libhwloc.jl @@ -17,12 +17,10 @@ const obj_types = :Bridge, :PCI_Device, :OS_Device, :Misc, :MemCache, :Die, :Error] const cache_types = Symbol[:Unified, :Data, :Instruction] -# const bridge_types +const bridge_types = [:Host, :PCI] const osdev_types = Symbol[:Block, :GPU, :Network, :Openfabrics, :DMA, :CoProc] - - struct hwloc_obj_memory_page_type_s size::Culonglong count::Culonglong @@ -232,11 +230,11 @@ function load_attr(hattr::Ptr{Cvoid}, type_::Symbol) elseif type_==:Misc error("not implemented") elseif type_==:Bridge - error("not implemented") + return NullAttr() elseif type_==:PCI_Device - error("not implemented") + return NullAttr() elseif type_==:OS_Device - error("not implemented") + return NullAttr() elseif type_==:Die ha = unsafe_load(convert(Ptr{hwloc_cache_attr_s}, hattr)) return DieAttr(ha.depth) @@ -265,6 +263,8 @@ struct Object memory_children::Vector{Object} + io_children::Vector{Object} + # Object() = new(:Error, -1, "(nothing)", NullAttr(), # 0, -1, -1, # -1, # Object[], Object[]) @@ -321,25 +321,69 @@ function load(hobj::hwloc_obj_t) children = Object[load(child) for child in obj_children] memory_children = Object[] - if obj.memory_arity != C_NULL && obj.memory_first_child != C_NULL + if obj.memory_arity != 0 && obj.memory_first_child != C_NULL push!(memory_children, load(obj.memory_first_child)) end - topo = Object(type_, os_index, name, attr, mem, depth, logical_index, children, memory_children) + io_children = Object[] + @show obj.io_arity + if obj.io_arity != 0 + io_child = obj.io_first_child + while io_child != C_NULL + push!(io_children, load(obj.io_first_child)) + io_child = unsafe_load(io_child).next_sibling + end + end + @show io_children + + topo = Object(type_, os_index, name, attr, mem, depth, logical_index, children, memory_children, io_children) return topo end +# TODO enum +const HWLOC_TYPE_FILTER_KEEP_ALL = Cint(0) +const HWLOC_TYPE_FILTER_KEEP_NONE = Cint(1) +const HWLOC_TYPE_FILTER_KEEP_STRUCTURE = Cint(2) +const HWLOC_TYPE_FILTER_KEEP_IMPORTANT = Cint(3) + +function get_type_filter(topology, object_type) + r_type_filter = Ref{Cint}() + ierr = ccall((:hwloc_topology_get_type_filter, libhwloc), Cint, (Ptr{Cvoid}, Cint, Ptr{Cint}), topology, object_type, r_type_filter) + @assert ierr == 0 + return r_type_filter[] +end + +function set_type_filter!(topology, object_type, type_filter) + ierr = ccall((:hwloc_topology_set_type_filter, libhwloc), Cint, (Ptr{Cvoid}, Cint, Cint), topology, object_type, type_filter) + @assert ierr == 0 + return +end + +function set_io_types_filter!(htopo, filter) + os_filter_type = findfirst(s->s==:OS_Device, Hwloc.obj_types) - 1 + set_type_filter!(htopo, os_filter_type, filter) + set_type_filter!(htopo, os_filter_type-1, filter) + set_type_filter!(htopo, os_filter_type-2, filter) +end + +function topology_init() + r_htopo = Ref{Ptr{Cvoid}}() + ierr = ccall((:hwloc_topology_init, libhwloc), Cint, (Ptr{Ptr{Cvoid}},), r_htopo) + @assert ierr == 0 + return r_htopo[] +end + +function topology_destroy(htopo) + ccall((:hwloc_topology_destroy, libhwloc), Cvoid, (Ptr{Cvoid},), htopo) +end + """ topology_load() -> Hwloc.Object Load the system topology by calling into libhwloc. """ -function topology_load() - htopop = Ref{Ptr{Cvoid}}() - ierr = ccall((:hwloc_topology_init, libhwloc), Cint, (Ptr{Cvoid},), htopop) - @assert ierr==0 - htopo = htopop[] +function topology_load(htopo=topology_init(); destroy=true) ierr = ccall((:hwloc_topology_load, libhwloc), Cint, (Ptr{Cvoid},), htopo) @assert ierr==0 @@ -353,7 +397,9 @@ function topology_load() (Ptr{Cvoid}, Cuint, Cuint), htopo, 0, 0) topo = load(root) - ccall((:hwloc_topology_destroy, libhwloc), Cvoid, (Ptr{Cvoid},), htopo) + if destroy + topology_destroy(htopo) + end return topo end \ No newline at end of file From 92310dac29e76ceb31abc54074ab7ab6a2f42dcf Mon Sep 17 00:00:00 2001 From: Valentin Churavy Date: Sun, 13 Nov 2022 14:59:49 -0500 Subject: [PATCH 02/36] fixup! WIP: Add support for PCI devices --- src/libhwloc.jl | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/src/libhwloc.jl b/src/libhwloc.jl index 21fd761..b24a3aa 100644 --- a/src/libhwloc.jl +++ b/src/libhwloc.jl @@ -321,20 +321,22 @@ function load(hobj::hwloc_obj_t) children = Object[load(child) for child in obj_children] memory_children = Object[] - if obj.memory_arity != 0 && obj.memory_first_child != C_NULL - push!(memory_children, load(obj.memory_first_child)) + if obj.memory_arity != 0 + memory_child = obj.memory_first_child + while memory_child != C_NULL + push!(memory_children, load(memory_child)) + memory_child = unsafe_load(memory_child).next_sibling + end end io_children = Object[] - @show obj.io_arity if obj.io_arity != 0 io_child = obj.io_first_child while io_child != C_NULL - push!(io_children, load(obj.io_first_child)) + push!(io_children, load(io_child)) io_child = unsafe_load(io_child).next_sibling end end - @show io_children topo = Object(type_, os_index, name, attr, mem, depth, logical_index, children, memory_children, io_children) return topo From 569e052c2b68f6e2544d84c170ccd69a072067e1 Mon Sep 17 00:00:00 2001 From: Valentin Churavy Date: Sun, 13 Nov 2022 15:08:21 -0500 Subject: [PATCH 03/36] Add PCIDevAttr --- src/libhwloc.jl | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/src/libhwloc.jl b/src/libhwloc.jl index b24a3aa..82f3c62 100644 --- a/src/libhwloc.jl +++ b/src/libhwloc.jl @@ -183,6 +183,12 @@ struct PCIDevAttr <: Attribute revision::Int linkspeed::Float32 end +function PCIDevAttr(ha::hwloc_pcidev_attr_s) + return PCIDevAttr( + ha.domain, ha.bus, ha.dev, ha.func, ha.class_id, + ha.vendor_id, ha.device_id, ha.subvendor_id, ha.subdevice_id, + ha.revision, ha.linkspeed) +end # TODO: expand this show(io::IO, a::PCIDevAttr) = print(io, "PCIDev{...}") @@ -232,7 +238,8 @@ function load_attr(hattr::Ptr{Cvoid}, type_::Symbol) elseif type_==:Bridge return NullAttr() elseif type_==:PCI_Device - return NullAttr() + ha = unsafe_load(convert(Ptr{hwloc_pcidev_attr_s}, hattr)) + return PCIDevAttr(ha) elseif type_==:OS_Device return NullAttr() elseif type_==:Die From 331f9b4cc7ddcb4addd2580f8bb3c49cbc6e08e8 Mon Sep 17 00:00:00 2001 From: Johannes Blaschke Date: Tue, 3 Jan 2023 23:08:27 +1300 Subject: [PATCH 04/36] wip on printing pci data --- src/highlevel_api.jl | 21 ++++++++++++++------- src/lowlevel_api.jl | 14 +++++++++----- 2 files changed, 23 insertions(+), 12 deletions(-) diff --git a/src/highlevel_api.jl b/src/highlevel_api.jl index d8d8329..8c1ed60 100644 --- a/src/highlevel_api.jl +++ b/src/highlevel_api.jl @@ -50,6 +50,10 @@ function print_topology(io::IO = stdout, obj::Object = gettopology(); indent = " print_topology(io, child; indent = indent*repeat(" ", 4), newline=true) end end + + for child in obj.io_children + print_topology(io, child; indent=indent*repeat(" ", 4), newline=true) + end return nothing end print_topology(obj::Object) = print_topology(stdout, obj) @@ -57,12 +61,15 @@ print_topology(obj::Object) = print_topology(stdout, obj) """ Returns the top-level system topology `Object`. -On first call, it loads the topology by querying -libhwloc and caches the result. +On first call, it loads the topology by querying libhwloc and caches the result. +Pass `reload=true` in order to force reload. """ -function gettopology() - if !isassigned(machine_topology) - machine_topology[] = topology_load() +function gettopology(htopo=nothing; reload=false) + if reload || (!isassigned(machine_topology)) + if isnothing(htopo) + htopo=topology_init() + end + machine_topology[] = topology_load(htopo) end return machine_topology[] @@ -71,7 +78,7 @@ end """ Prints the system topology as a tree. """ -topology() = print_topology(gettopology()) +topology(htopo=nothing) = print_topology(gettopology(htopo)) """ Prints a summary of the system topology (loosely similar to `hwloc-info`). @@ -306,6 +313,6 @@ The quality of the result might depend on the used terminal and might vary betwe **Note:** The specific visualization may change between minor versions. """ function topology_graphical() - run(`$(lstopo_no_graphics()) --no-io --no-legend --of txt`) + run(`$(lstopo_no_graphics()) --no-legend --of txt`) return nothing end diff --git a/src/lowlevel_api.jl b/src/lowlevel_api.jl index e4aa6bf..ca03678 100644 --- a/src/lowlevel_api.jl +++ b/src/lowlevel_api.jl @@ -98,7 +98,9 @@ function PCIDevAttr(ha::hwloc_pcidev_attr_s) ha.revision, ha.linkspeed) end # TODO: expand this -show(io::IO, a::PCIDevAttr) = print(io, "PCIDev{...}") +function show(io::IO, a::PCIDevAttr) + print(io, "PCIDev(domain=$(a.domain), bus=$(a.bus), func=$(a.func), class_id=$(a.class_id), vendor_id=$(a.vendor_id), device_id=$(a.device_id), subvendor_id=$(a.subvendor_id), subdevice_id=$(a.subdevice_id), revision=$(a.revision), linkspeed=$(a.linkspeed))") +end # type BridgeAttr <: Attribute end @@ -234,9 +236,6 @@ function load(hobj::hwloc_obj_t) children = Object[load(child) for child in obj_children] memory_children = Object[] - # if obj.memory_arity != C_NULL && obj.memory_first_child != C_NULL - # push!(memory_children, load(obj.memory_first_child)) - # end if obj.memory_arity != 0 memory_child = obj.memory_first_child while memory_child != C_NULL @@ -262,9 +261,14 @@ function load(hobj::hwloc_obj_t) end -function topology_init() +function topology_init(;get_io=true) r_htopo = Ref{hwloc_topology_t}() hwloc_topology_init(r_htopo) + if get_io + hwloc_topology_set_io_types_filter( + r_htopo[], LibHwloc.HWLOC_TYPE_FILTER_KEEP_ALL + ) + end return r_htopo[] end From 41c5f2013d452a81c5ec729e6fa89833c78f68f5 Mon Sep 17 00:00:00 2001 From: Johannes Blaschke Date: Sun, 9 Jul 2023 22:06:52 -0700 Subject: [PATCH 05/36] wip --- src/libhwloc.jl | 2 +- src/lowlevel_api.jl | 65 ++++++++++++++++++++++++++++++++------------- 2 files changed, 48 insertions(+), 19 deletions(-) diff --git a/src/libhwloc.jl b/src/libhwloc.jl index 4855794..4510d1e 100644 --- a/src/libhwloc.jl +++ b/src/libhwloc.jl @@ -235,7 +235,7 @@ const hwloc_nodeset_t = hwloc_bitmap_t const hwloc_const_nodeset_t = hwloc_const_bitmap_t -# TODO: doe we need this? +# TODO: do we need this? # struct hwloc_obj_memory_page_type_s # size::Culonglong # count::Culonglong diff --git a/src/lowlevel_api.jl b/src/lowlevel_api.jl index ca03678..eb23754 100644 --- a/src/lowlevel_api.jl +++ b/src/lowlevel_api.jl @@ -2,12 +2,12 @@ using ..LibHwloc: hwloc_cpuset_t, hwloc_nodeset_t, hwloc_obj_type_t, hwloc_obj_cache_type_t, hwloc_obj_bridge_type_t, hwloc_obj_osdev_type_t, hwloc_distances_s, hwloc_obj, hwloc_obj_t, hwloc_obj_attr_u, hwloc_cache_attr_s, - hwloc_group_attr_s, hwloc_pcidev_attr_s, hwloc_osdev_attr_s, - hwloc_topology_t, hwloc_topology_init, hwloc_topology_load, - hwloc_topology_get_depth, hwloc_get_nbobjs_by_depth, hwloc_get_obj_by_depth, - hwloc_topology_destroy, hwloc_type_filter_e, hwloc_topology_set_type_filter, - hwloc_topology_get_type_filter, hwloc_topology_set_all_types_filter, - hwloc_topology_set_cache_types_filter, + hwloc_group_attr_s, hwloc_bridge_attr_s, hwloc_pcidev_attr_s, + hwloc_osdev_attr_s, hwloc_topology_t, hwloc_topology_init, + hwloc_topology_load, hwloc_topology_get_depth, hwloc_get_nbobjs_by_depth, + hwloc_get_obj_by_depth, hwloc_topology_destroy, hwloc_type_filter_e, + hwloc_topology_set_type_filter, hwloc_topology_get_type_filter, + hwloc_topology_set_all_types_filter, hwloc_topology_set_cache_types_filter, hwloc_topology_set_icache_types_filter, hwloc_topology_set_io_types_filter, hwloc_topology_set_userdata, hwloc_topology_get_userdata @@ -42,15 +42,14 @@ for x in instances(hwloc_obj_cache_type_t) push!(cache_types, cenum_name_to_symbol(x, "HWLOC_OBJ_CACHE_")) end -# const bridge_types bridge_types = Symbol[] for x in instances(hwloc_obj_bridge_type_t) - push!(cache_types, cenum_name_to_symbol(x, "HWLOC_OBJ_BRIDGE_")) + push!(bridge_types, cenum_name_to_symbol(x, "HWLOC_OBJ_BRIDGE_")) end osdev_types = Symbol[] for x in instances(hwloc_obj_osdev_type_t) - push!(cache_types, cenum_name_to_symbol(x, "HWLOC_OBJ_OSDEV_")) + push!(osdev_types, cenum_name_to_symbol(x, "HWLOC_OBJ_OSDEV_")) end abstract type Attribute end @@ -67,8 +66,14 @@ struct CacheAttr <: Attribute type_::Symbol end function show(io::IO, a::CacheAttr) - print(io, "Cache{size=$(a.size),depth=$(a.depth),linesize=$(a.linesize),", - "associativity=$(a.associativity),type=$(string(a.type_))}") + print( + io, + "Cache{size=$(a.size), " * + "depth=$(a.depth), " * + "linesize=$(a.linesize), " * + "associativity=$(a.associativity), " * + "type=$(string(a.type_))}" + ) end struct GroupAttr <: Attribute @@ -93,16 +98,36 @@ struct PCIDevAttr <: Attribute end function PCIDevAttr(ha::hwloc_pcidev_attr_s) return PCIDevAttr( - ha.domain, ha.bus, ha.dev, ha.func, ha.class_id, - ha.vendor_id, ha.device_id, ha.subvendor_id, ha.subdevice_id, - ha.revision, ha.linkspeed) + ha.domain, ha.bus, ha.dev, ha.func, ha.class_id, ha.vendor_id, + ha.device_id, ha.subvendor_id, ha.subdevice_id, ha.revision, + ha.linkspeed + ) end -# TODO: expand this function show(io::IO, a::PCIDevAttr) - print(io, "PCIDev(domain=$(a.domain), bus=$(a.bus), func=$(a.func), class_id=$(a.class_id), vendor_id=$(a.vendor_id), device_id=$(a.device_id), subvendor_id=$(a.subvendor_id), subdevice_id=$(a.subdevice_id), revision=$(a.revision), linkspeed=$(a.linkspeed))") + print( + io, + "PCIDev(domain=$(a.domain), " * + "bus=$(a.bus), " * + "func=$(a.func), " * + "class_id=$(a.class_id), " * + "vendor_id=$(a.vendor_id), " * + "device_id=$(a.device_id), " * + "subvendor_id=$(a.subvendor_id), " * + "subdevice_id=$(a.subdevice_id), " * + "revision=$(a.revision), " * + "linkspeed=$(a.linkspeed))" + ) end -# type BridgeAttr <: Attribute end +struct BridgeAttr <: Attribute + data::NTuple{40, UInt8} +end +function BridgeAttr(ha::hwloc_bridge_attr_s) + return BridgeAttr(ha.data) +end +function show(io::IO, a::BridgeAttr) + print(io, "BridgeAttr{data=$(string(a.data))}") +end struct OSDevAttr <: Attribute type_::Symbol @@ -146,9 +171,13 @@ function load_attr(hattr::Ptr{hwloc_obj_attr_u}, type_::Symbol) elseif type_==:Misc error("not implemented") elseif type_==:Bridge - return NullAttr() + ha = unsafe_load(convert(Ptr{hwloc_bridge_attr_s}, hattr)) + println(ha) + return BridgeAttr(ha) + # return NullAttr() elseif type_==:PCI_Device ha = unsafe_load(convert(Ptr{hwloc_pcidev_attr_s}, hattr)) + println(ha) return PCIDevAttr(ha) elseif type_==:OS_Device return NullAttr() From 74096db9f0c435ba7199e7df4cf75170119f9eb1 Mon Sep 17 00:00:00 2001 From: Johannes Blaschke Date: Thu, 13 Jul 2023 22:57:10 -0700 Subject: [PATCH 06/36] extract more pci data --- src/lowlevel_api.jl | 27 ++++++++++++++++++++------- 1 file changed, 20 insertions(+), 7 deletions(-) diff --git a/src/lowlevel_api.jl b/src/lowlevel_api.jl index eb23754..d8b5cae 100644 --- a/src/lowlevel_api.jl +++ b/src/lowlevel_api.jl @@ -9,7 +9,8 @@ using ..LibHwloc: hwloc_topology_set_type_filter, hwloc_topology_get_type_filter, hwloc_topology_set_all_types_filter, hwloc_topology_set_cache_types_filter, hwloc_topology_set_icache_types_filter, hwloc_topology_set_io_types_filter, - hwloc_topology_set_userdata, hwloc_topology_get_userdata + hwloc_topology_set_userdata, hwloc_topology_get_userdata, + var"##Ctag#349", var"##Ctag#350" # List of special capitalizations -- cenum_name_to_symbol will by default # convert the all-uppcase C enum name to lowercase (with capitalized leading @@ -120,13 +121,28 @@ function show(io::IO, a::PCIDevAttr) end struct BridgeAttr <: Attribute - data::NTuple{40, UInt8} + upstream::var"##Ctag#349" + upstream_type::hwloc_obj_bridge_type_t + downstream::var"##Ctag#350" + downstream_type::hwloc_obj_bridge_type_t + depth::UInt end function BridgeAttr(ha::hwloc_bridge_attr_s) - return BridgeAttr(ha.data) + return BridgeAttr( + ha.upstream, ha.upstream_type, + ha.downstream, ha.downstream_type, + ha.depth + ) end function show(io::IO, a::BridgeAttr) - print(io, "BridgeAttr{data=$(string(a.data))}") + print( + io, + "BridgeAttr{upstream=$(string(a.upstream)), " * + "upstream_type=$(string(a.upstream_type)), " * + "downstream=$(string(a.downstream)), " * + "downstream_type=$(string(a.downstream_type)), " * + "}" + ) end struct OSDevAttr <: Attribute @@ -172,12 +188,9 @@ function load_attr(hattr::Ptr{hwloc_obj_attr_u}, type_::Symbol) error("not implemented") elseif type_==:Bridge ha = unsafe_load(convert(Ptr{hwloc_bridge_attr_s}, hattr)) - println(ha) return BridgeAttr(ha) - # return NullAttr() elseif type_==:PCI_Device ha = unsafe_load(convert(Ptr{hwloc_pcidev_attr_s}, hattr)) - println(ha) return PCIDevAttr(ha) elseif type_==:OS_Device return NullAttr() From c95c25d336d0148ec00a53b12de941d9c9010c18 Mon Sep 17 00:00:00 2001 From: Johannes Blaschke Date: Sun, 16 Jul 2023 21:49:03 -0700 Subject: [PATCH 07/36] read dev class id --- src/Hwloc.jl | 1 + src/libhwloc_extensions.jl | 17 +++++++++++++++++ src/lowlevel_api.jl | 15 +++++++++------ 3 files changed, 27 insertions(+), 6 deletions(-) create mode 100644 src/libhwloc_extensions.jl diff --git a/src/Hwloc.jl b/src/Hwloc.jl index 9bcbc00..1255d08 100644 --- a/src/Hwloc.jl +++ b/src/Hwloc.jl @@ -5,6 +5,7 @@ using Statistics import Base: show, IteratorSize, IteratorEltype, isempty, eltype, iterate include("libhwloc.jl") +include("libhwloc_extensions.jl") include("lowlevel_api.jl") include("highlevel_api.jl") diff --git a/src/libhwloc_extensions.jl b/src/libhwloc_extensions.jl new file mode 100644 index 0000000..bb5d5b0 --- /dev/null +++ b/src/libhwloc_extensions.jl @@ -0,0 +1,17 @@ +module LibHwlocExtensions + +using ..LibHwloc: libhwloc, Hwloc_jll + +using CEnum + +function hwloc_pci_class_string(class_id) + val = ccall( + (:hwloc_pci_class_string, libhwloc), + Ptr{Cchar}, + (Cushort,), + class_id + ) + return unsafe_string(val) +end + +end \ No newline at end of file diff --git a/src/lowlevel_api.jl b/src/lowlevel_api.jl index d8b5cae..6ea3c4e 100644 --- a/src/lowlevel_api.jl +++ b/src/lowlevel_api.jl @@ -12,6 +12,9 @@ using ..LibHwloc: hwloc_topology_set_userdata, hwloc_topology_get_userdata, var"##Ctag#349", var"##Ctag#350" +using ..LibHwlocExtensions: + hwloc_pci_class_string + # List of special capitalizations -- cenum_name_to_symbol will by default # convert the all-uppcase C enum name to lowercase (with capitalized leading # character). Any names listed here will be capitalized as stated below: @@ -110,7 +113,7 @@ function show(io::IO, a::PCIDevAttr) "PCIDev(domain=$(a.domain), " * "bus=$(a.bus), " * "func=$(a.func), " * - "class_id=$(a.class_id), " * + "class_id=$(hwloc_pci_class_string(a.class_id)), " * "vendor_id=$(a.vendor_id), " * "device_id=$(a.device_id), " * "subvendor_id=$(a.subvendor_id), " * @@ -137,11 +140,10 @@ end function show(io::IO, a::BridgeAttr) print( io, - "BridgeAttr{upstream=$(string(a.upstream)), " * - "upstream_type=$(string(a.upstream_type)), " * - "downstream=$(string(a.downstream)), " * - "downstream_type=$(string(a.downstream_type)), " * - "}" + "BridgeAttr(US=$(hwloc_pci_class_string(a.upstream.pci.class_id)), " * + "upstream_type=$(string(a.upstream_type)), " * + "downstream_type=$(string(a.downstream_type)) " * + ")" ) end @@ -260,6 +262,7 @@ function load(hobj::hwloc_obj_t) os_index = mod(obj.os_index, Cint) name = obj.name == C_NULL ? "" : unsafe_string(obj.name) + println(name) attr = load_attr(obj.attr, type_) From f54ee3e518101209cc4a4990609e844d48e2a063 Mon Sep 17 00:00:00 2001 From: Johannes Blaschke Date: Thu, 20 Jul 2023 23:15:23 -0700 Subject: [PATCH 08/36] wip on inserting process information --- src/highlevel_api.jl | 18 ++++++++++++++---- src/lowlevel_api.jl | 28 ++++++++++++++++++---------- 2 files changed, 32 insertions(+), 14 deletions(-) diff --git a/src/highlevel_api.jl b/src/highlevel_api.jl index 8c1ed60..b947e4a 100644 --- a/src/highlevel_api.jl +++ b/src/highlevel_api.jl @@ -1,4 +1,4 @@ -using ..LibHwloc: hwloc_get_api_version +using ..LibHwloc: hwloc_get_api_version, HWLOC_OBJ_BRIDGE_HOST """ @@ -21,20 +21,30 @@ Prints the topology of the given `obj` as a tree to `io`. """ function print_topology(io::IO = stdout, obj::Object = gettopology(); indent = "", newline = false, prefix = "") t = hwloc_typeof(obj) + + # println("t=$(t) name=$(obj.name)") + idxstr = t in (:Package, :Core, :PU) ? "L#$(obj.logical_index) P#$(obj.os_index) " : "" attrstr = string(obj.attr) if t in (:L1Cache, :L2Cache, :L3Cache, :L1ICache) tstr = first(string(t), 2) attrstr = "("*_bytes2string(obj.attr.size)*")" + elseif t == :Bridge + if obj.attr.upstream_type == HWLOC_OBJ_BRIDGE_HOST + tstr = "HostBridge" + else + tstr = "PCIBridge" + end else tstr = string(t) end newline && print(io, "\n", indent) - print(io, prefix, tstr, " ", - idxstr, - attrstr, obj.mem > 0 ? "("*_bytes2string(obj.mem)*")" : "") + print( + io, prefix, tstr, " ", idxstr, attrstr, + obj.mem > 0 ? "("*_bytes2string(obj.mem)*")" : "" + ) for memchild in obj.memory_children memstr = "("*_bytes2string(memchild.mem)*")" diff --git a/src/lowlevel_api.jl b/src/lowlevel_api.jl index 6ea3c4e..654e3ce 100644 --- a/src/lowlevel_api.jl +++ b/src/lowlevel_api.jl @@ -110,15 +110,15 @@ end function show(io::IO, a::PCIDevAttr) print( io, - "PCIDev(domain=$(a.domain), " * - "bus=$(a.bus), " * - "func=$(a.func), " * - "class_id=$(hwloc_pci_class_string(a.class_id)), " * - "vendor_id=$(a.vendor_id), " * - "device_id=$(a.device_id), " * - "subvendor_id=$(a.subvendor_id), " * - "subdevice_id=$(a.subdevice_id), " * - "revision=$(a.revision), " * + "PCIDev(domain=$(a.domain), " * + "bus=$(a.bus), " * + "func=$(a.func), " * + "class_id=$(hwloc_pci_class_string(a.class_id)), " * + "vendor_id=$(a.vendor_id), " * + "device_id=$(a.device_id), " * + "subvendor_id=$(a.subvendor_id), " * + "subdevice_id=$(a.subdevice_id), " * + "revision=$(a.revision), " * "linkspeed=$(a.linkspeed))" ) end @@ -262,7 +262,6 @@ function load(hobj::hwloc_obj_t) os_index = mod(obj.os_index, Cint) name = obj.name == C_NULL ? "" : unsafe_string(obj.name) - println(name) attr = load_attr(obj.attr, type_) @@ -298,6 +297,15 @@ function load(hobj::hwloc_obj_t) end end + misc_children = Object[] + if obj.misc_arity != 0 + misc_child = obj.misc_first_child + while misc_child != C_NULL + push!(misc_children, load(misc_child)) + misc_child = unsafe_load(misc_child).next_sibling + end + end + topo = Object( type_, os_index, name, attr, mem, depth, logical_index, children, memory_children, io_children From 448948cc5b8c5108b47f895eabbf8740d3139da1 Mon Sep 17 00:00:00 2001 From: Johannes Blaschke Date: Sat, 22 Jul 2023 21:32:17 -0700 Subject: [PATCH 09/36] print PCI devices properly now --- Project.toml | 3 ++- src/highlevel_api.jl | 39 ++++++++++++++++++++++++++++++++------- src/lowlevel_api.jl | 1 + 3 files changed, 35 insertions(+), 8 deletions(-) diff --git a/Project.toml b/Project.toml index 21819ea..c249720 100644 --- a/Project.toml +++ b/Project.toml @@ -6,12 +6,13 @@ version = "3.0.0" [deps] CEnum = "fa961155-64e5-5f13-b03f-caf6b980ea82" Hwloc_jll = "e33a78d0-f292-5ffc-b300-72abe9b543c8" +Printf = "de0858da-6303-5e67-8744-51eddeeeb8d7" Statistics = "10745b16-79ce-11e8-11f9-7d13ad32a3b2" [compat] +CEnum = "0.4" Hwloc_jll = "2.8" julia = "1.6" -CEnum = "0.4" [extras] CpuId = "adafc99b-e345-5852-983c-f28acb93d879" diff --git a/src/highlevel_api.jl b/src/highlevel_api.jl index b947e4a..9151676 100644 --- a/src/highlevel_api.jl +++ b/src/highlevel_api.jl @@ -1,5 +1,6 @@ using ..LibHwloc: hwloc_get_api_version, HWLOC_OBJ_BRIDGE_HOST +using Printf """ Returns the API version of libhwloc. @@ -14,12 +15,17 @@ function get_api_version() VersionNumber(major, minor, patch) end +const minimal_classes = ["VGA", "NVMExp", "Network"] + """ print_topology([io::IO = stdout, obj::Object = gettopology()]) Prints the topology of the given `obj` as a tree to `io`. """ -function print_topology(io::IO = stdout, obj::Object = gettopology(); indent = "", newline = false, prefix = "") +function print_topology( + io::IO = stdout, obj::Object = gettopology(); + indent = "", newline = false, prefix = "", minimal=true + ) t = hwloc_typeof(obj) # println("t=$(t) name=$(obj.name)") @@ -27,24 +33,43 @@ function print_topology(io::IO = stdout, obj::Object = gettopology(); indent = " idxstr = t in (:Package, :Core, :PU) ? "L#$(obj.logical_index) P#$(obj.os_index) " : "" attrstr = string(obj.attr) + # this is set to false whenever minimal == true and the PCI class_id strings + # don't match the minimal_classes list + print_device = true + if t in (:L1Cache, :L2Cache, :L3Cache, :L1ICache) tstr = first(string(t), 2) attrstr = "("*_bytes2string(obj.attr.size)*")" elseif t == :Bridge if obj.attr.upstream_type == HWLOC_OBJ_BRIDGE_HOST - tstr = "HostBridge" + tstr = "HostBridge" + attrstr = "" else tstr = "PCIBridge" + attrstr = "" + end + elseif t == :PCI_Device + class_string = hwloc_pci_class_string(obj.attr.class_id) + tstr = "PCI" + attrstr = @sprintf( + "%s%02x:%02x.%01x", + Char(obj.attr.domain), obj.attr.bus, obj.attr.dev, obj.attr.func + ) * " ($(class_string))" + if minimal + print_device = (class_string in minimal_classes) end else tstr = string(t) + attrstr = obj.name end - newline && print(io, "\n", indent) - print( - io, prefix, tstr, " ", idxstr, attrstr, - obj.mem > 0 ? "("*_bytes2string(obj.mem)*")" : "" - ) + if print_device + newline && print(io, "\n", indent) + print( + io, prefix, tstr, " ", idxstr, attrstr, + obj.mem > 0 ? "("*_bytes2string(obj.mem)*")" : "" + ) + end for memchild in obj.memory_children memstr = "("*_bytes2string(memchild.mem)*")" diff --git a/src/lowlevel_api.jl b/src/lowlevel_api.jl index 654e3ce..46a408d 100644 --- a/src/lowlevel_api.jl +++ b/src/lowlevel_api.jl @@ -112,6 +112,7 @@ function show(io::IO, a::PCIDevAttr) io, "PCIDev(domain=$(a.domain), " * "bus=$(a.bus), " * + "dev=$(a.dev), " * "func=$(a.func), " * "class_id=$(hwloc_pci_class_string(a.class_id)), " * "vendor_id=$(a.vendor_id), " * From f0aa7f8c50487a5bd352fa8315b803c6fd9a42c6 Mon Sep 17 00:00:00 2001 From: Johannes Blaschke Date: Sun, 23 Jul 2023 16:35:14 -0700 Subject: [PATCH 10/36] get lstopo-style printing --- src/highlevel_api.jl | 56 ++++++++++++++++++++++++++++++++++++++------ src/lowlevel_api.jl | 17 ++++++++------ 2 files changed, 59 insertions(+), 14 deletions(-) diff --git a/src/highlevel_api.jl b/src/highlevel_api.jl index 9151676..fc03336 100644 --- a/src/highlevel_api.jl +++ b/src/highlevel_api.jl @@ -1,4 +1,6 @@ -using ..LibHwloc: hwloc_get_api_version, HWLOC_OBJ_BRIDGE_HOST +using ..LibHwloc: hwloc_get_api_version, HWLOC_OBJ_BRIDGE_HOST, + HWLOC_OBJ_OSDEV_BLOCK, HWLOC_OBJ_OSDEV_GPU, HWLOC_OBJ_OSDEV_NETWORK, + HWLOC_OBJ_OSDEV_OPENFABRICS, HWLOC_OBJ_OSDEV_DMA, HWLOC_OBJ_OSDEV_COPROC using Printf @@ -15,7 +17,32 @@ function get_api_version() VersionNumber(major, minor, patch) end -const minimal_classes = ["VGA", "NVMExp", "Network"] +const minimal_classes = ["VGA", "NVMExp", "Network", "Other"] +subtype_str(obj) = obj.subtype == "" ? "" : "($(obj.subtype))" + +function is_visible(obj::Object; minimal=true) + t = hwloc_typeof(obj) + + if t == :Bridge + for child in obj.io_children + if is_visible(child) + return true + end + end + return false + end + + if t == :PCI_Device + class_string = hwloc_pci_class_string(obj.attr.class_id) + if minimal + return class_string in minimal_classes + else + return true + end + end + + return true +end """ print_topology([io::IO = stdout, obj::Object = gettopology()]) @@ -28,14 +55,12 @@ function print_topology( ) t = hwloc_typeof(obj) - # println("t=$(t) name=$(obj.name)") - idxstr = t in (:Package, :Core, :PU) ? "L#$(obj.logical_index) P#$(obj.os_index) " : "" attrstr = string(obj.attr) # this is set to false whenever minimal == true and the PCI class_id strings # don't match the minimal_classes list - print_device = true + print_device = is_visible(obj; minimal=minimal) if t in (:L1Cache, :L2Cache, :L3Cache, :L1ICache) tstr = first(string(t), 2) @@ -55,8 +80,22 @@ function print_topology( "%s%02x:%02x.%01x", Char(obj.attr.domain), obj.attr.bus, obj.attr.dev, obj.attr.func ) * " ($(class_string))" - if minimal - print_device = (class_string in minimal_classes) + elseif t == :OS_Device + attrstr = "\"$(obj.name)\"" + tstr = string(t) * " " * if obj.attr.type == HWLOC_OBJ_OSDEV_BLOCK + "Block$(subtype_str(obj))" + elseif obj.attr.type == HWLOC_OBJ_OSDEV_GPU + "GPU" + elseif obj.attr.type == HWLOC_OBJ_OSDEV_NETWORK + "Net" + elseif obj.attr.type == HWLOC_OBJ_OSDEV_OPENFABRICS + "OpenFabrics" + elseif obj.attr.type == HWLOC_OBJ_OSDEV_DMA + "DMA" + elseif obj.attr.type == HWLOC_OBJ_OSDEV_COPROC + "CoProc" + else + string(obj.attr) end else tstr = string(t) @@ -69,6 +108,8 @@ function print_topology( io, prefix, tstr, " ", idxstr, attrstr, obj.mem > 0 ? "("*_bytes2string(obj.mem)*")" : "" ) + else + return nothing end for memchild in obj.memory_children @@ -89,6 +130,7 @@ function print_topology( for child in obj.io_children print_topology(io, child; indent=indent*repeat(" ", 4), newline=true) end + return nothing end print_topology(obj::Object) = print_topology(stdout, obj) diff --git a/src/lowlevel_api.jl b/src/lowlevel_api.jl index 46a408d..df2df55 100644 --- a/src/lowlevel_api.jl +++ b/src/lowlevel_api.jl @@ -9,8 +9,8 @@ using ..LibHwloc: hwloc_topology_set_type_filter, hwloc_topology_get_type_filter, hwloc_topology_set_all_types_filter, hwloc_topology_set_cache_types_filter, hwloc_topology_set_icache_types_filter, hwloc_topology_set_io_types_filter, - hwloc_topology_set_userdata, hwloc_topology_get_userdata, - var"##Ctag#349", var"##Ctag#350" + hwloc_topology_set_userdata, hwloc_topology_get_userdata, var"##Ctag#349", + var"##Ctag#350" using ..LibHwlocExtensions: hwloc_pci_class_string @@ -149,10 +149,10 @@ function show(io::IO, a::BridgeAttr) end struct OSDevAttr <: Attribute - type_::Symbol + type::hwloc_obj_osdev_type_t end function show(io::IO, a::OSDevAttr) - print(io, "OSDev{type=$(string(a.type_))}") + print(io, "OSDev{type=$(string(a.type))}") end struct DieAttr <: Attribute @@ -196,7 +196,8 @@ function load_attr(hattr::Ptr{hwloc_obj_attr_u}, type_::Symbol) ha = unsafe_load(convert(Ptr{hwloc_pcidev_attr_s}, hattr)) return PCIDevAttr(ha) elseif type_==:OS_Device - return NullAttr() + ha = unsafe_load(convert(Ptr{hwloc_obj_osdev_type_t}, hattr)) + return OSDevAttr(ha) elseif type_==:Die ha = unsafe_load(convert(Ptr{hwloc_cache_attr_s}, hattr)) return DieAttr(ha.depth) @@ -210,6 +211,7 @@ end struct Object type_::Symbol + subtype::String os_index::Int name::String attr::Attribute @@ -259,6 +261,7 @@ function load(hobj::hwloc_obj_t) @assert Integer(obj.type)>=0 && Integer(obj.type) Date: Sun, 23 Jul 2023 17:02:25 -0700 Subject: [PATCH 11/36] add coproc subtype --- src/highlevel_api.jl | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/highlevel_api.jl b/src/highlevel_api.jl index fc03336..2c50a6e 100644 --- a/src/highlevel_api.jl +++ b/src/highlevel_api.jl @@ -82,7 +82,7 @@ function print_topology( ) * " ($(class_string))" elseif t == :OS_Device attrstr = "\"$(obj.name)\"" - tstr = string(t) * " " * if obj.attr.type == HWLOC_OBJ_OSDEV_BLOCK + tstr = if obj.attr.type == HWLOC_OBJ_OSDEV_BLOCK "Block$(subtype_str(obj))" elseif obj.attr.type == HWLOC_OBJ_OSDEV_GPU "GPU" @@ -93,7 +93,7 @@ function print_topology( elseif obj.attr.type == HWLOC_OBJ_OSDEV_DMA "DMA" elseif obj.attr.type == HWLOC_OBJ_OSDEV_COPROC - "CoProc" + "CoProc$(subtype_str(obj))" else string(obj.attr) end From 7b10523e18428c90ebe36482b0f7e211b7f632e4 Mon Sep 17 00:00:00 2001 From: Johannes Blaschke Date: Sun, 23 Jul 2023 20:10:50 -0700 Subject: [PATCH 12/36] update list of minimal device types --- Project.toml | 2 +- src/highlevel_api.jl | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Project.toml b/Project.toml index c249720..c76d7d9 100644 --- a/Project.toml +++ b/Project.toml @@ -1,7 +1,7 @@ name = "Hwloc" uuid = "0e44f5e4-bd66-52a0-8798-143a42290a1d" authors = ["Erik Schnetter "] -version = "3.0.0" +version = "3.1.0" [deps] CEnum = "fa961155-64e5-5f13-b03f-caf6b980ea82" diff --git a/src/highlevel_api.jl b/src/highlevel_api.jl index 2c50a6e..7d43a3b 100644 --- a/src/highlevel_api.jl +++ b/src/highlevel_api.jl @@ -17,7 +17,7 @@ function get_api_version() VersionNumber(major, minor, patch) end -const minimal_classes = ["VGA", "NVMExp", "Network", "Other"] +const minimal_classes = ["VGA", "NVMExp", "Network", "Ethernet", "Other"] subtype_str(obj) = obj.subtype == "" ? "" : "($(obj.subtype))" function is_visible(obj::Object; minimal=true) @@ -33,8 +33,8 @@ function is_visible(obj::Object; minimal=true) end if t == :PCI_Device - class_string = hwloc_pci_class_string(obj.attr.class_id) if minimal + class_string = hwloc_pci_class_string(obj.attr.class_id) return class_string in minimal_classes else return true From 3152c286e8e1ff1f67c97655f4a5bf3e9c429315 Mon Sep 17 00:00:00 2001 From: Johannes Blaschke Date: Sun, 23 Jul 2023 20:18:39 -0700 Subject: [PATCH 13/36] add more minimal devices --- src/highlevel_api.jl | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/highlevel_api.jl b/src/highlevel_api.jl index 7d43a3b..c180052 100644 --- a/src/highlevel_api.jl +++ b/src/highlevel_api.jl @@ -17,7 +17,9 @@ function get_api_version() VersionNumber(major, minor, patch) end -const minimal_classes = ["VGA", "NVMExp", "Network", "Ethernet", "Other"] +const minimal_classes = [ + "VGA", "NVMExp", "SATA", "Network", "Ethernet", "InfiniBand", "3D", "Other" +] subtype_str(obj) = obj.subtype == "" ? "" : "($(obj.subtype))" function is_visible(obj::Object; minimal=true) From 5fb034c080a3b568c524d338e9e72285925bd185 Mon Sep 17 00:00:00 2001 From: Johannes Blaschke Date: Mon, 24 Jul 2023 17:33:00 -0700 Subject: [PATCH 14/36] start working on trees --- Project.toml | 3 ++- src/Hwloc.jl | 2 ++ src/lowlevel_api.jl | 18 ++++++++++-------- src/tree.jl | 34 ++++++++++++++++++++++++++++++++++ 4 files changed, 48 insertions(+), 9 deletions(-) create mode 100644 src/tree.jl diff --git a/Project.toml b/Project.toml index c76d7d9..017d43c 100644 --- a/Project.toml +++ b/Project.toml @@ -1,9 +1,10 @@ name = "Hwloc" uuid = "0e44f5e4-bd66-52a0-8798-143a42290a1d" authors = ["Erik Schnetter "] -version = "3.1.0" +version = "3.1.1" [deps] +AbstractTrees = "1520ce14-60c1-5f80-bbc7-55ef81b5835c" CEnum = "fa961155-64e5-5f13-b03f-caf6b980ea82" Hwloc_jll = "e33a78d0-f292-5ffc-b300-72abe9b543c8" Printf = "de0858da-6303-5e67-8744-51eddeeeb8d7" diff --git a/src/Hwloc.jl b/src/Hwloc.jl index 1255d08..aff37da 100644 --- a/src/Hwloc.jl +++ b/src/Hwloc.jl @@ -8,11 +8,13 @@ include("libhwloc.jl") include("libhwloc_extensions.jl") include("lowlevel_api.jl") include("highlevel_api.jl") +include("tree.jl") export topology, gettopology, topology_info, getinfo, print_topology, topology_graphical export num_physical_cores, num_virtual_cores, num_packages, num_numa_nodes export cachesize, cachelinesize export hwloc_typeof, hwloc_isa, collectobjects +export HwlocTreeNode const machine_topology = Ref{Object}() diff --git a/src/lowlevel_api.jl b/src/lowlevel_api.jl index df2df55..e33072c 100644 --- a/src/lowlevel_api.jl +++ b/src/lowlevel_api.jl @@ -301,14 +301,16 @@ function load(hobj::hwloc_obj_t) end end - misc_children = Object[] - if obj.misc_arity != 0 - misc_child = obj.misc_first_child - while misc_child != C_NULL - push!(misc_children, load(misc_child)) - misc_child = unsafe_load(misc_child).next_sibling - end - end + # not needed for now -- unless we want to start putting things (like + # processes into the Hwloc tree) + # misc_children = Object[] + # if obj.misc_arity != 0 + # misc_child = obj.misc_first_child + # while misc_child != C_NULL + # push!(misc_children, load(misc_child)) + # misc_child = unsafe_load(misc_child).next_sibling + # end + # end topo = Object( type_, subtype, os_index, name, attr, mem, depth, logical_index, diff --git a/src/tree.jl b/src/tree.jl new file mode 100644 index 0000000..27df695 --- /dev/null +++ b/src/tree.jl @@ -0,0 +1,34 @@ +import AbstractTrees + +mutable struct HwlocTreeNode + object::Object + type::Symbol + + parent::Union{Nothing, HwlocTreeNode} + children::Vector{HwlocTreeNode} + memory_children::Vector{HwlocTreeNode} + io_children::Vector{HwlocTreeNode} + + function HwlocTreeNode(obj::Object; parent=nothing, type=nothing) + this = new(obj, obj.type_, parent) + + this.children = HwlocTreeNode.(obj.children; parent=this) + this.memory_children = HwlocTreeNode.(obj.memory_children; parent=this) + this.io_children = HwlocTreeNode.(obj.io_children; parent=this) + + return this + end +end + +function AbstractTrees.children(node::HwlocTreeNode) + tuple(node.children..., node.memory_children..., node.io_children...) +end + +AbstractTrees.nodevalue(n::HwlocTreeNode) = n.object + +AbstractTrees.ParentLinks(::Type{<:HwlocTreeNode}) = StoredParents() + +AbstractTrees.parent(n::HwlocTreeNode) = n.parent + +AbstractTrees.NodeType(::Type{<:HwlocTreeNode}) where {T} = HasNodeType() +AbstractTrees.nodetype(::Type{<:HwlocTreeNode}) where {T} = HwlocTreeNode \ No newline at end of file From 9b688815849cf02f6d796cd710badb57e17ea4c0 Mon Sep 17 00:00:00 2001 From: Johannes Blaschke Date: Mon, 24 Jul 2023 17:35:30 -0700 Subject: [PATCH 15/36] fix --- src/tree.jl | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/tree.jl b/src/tree.jl index 27df695..2778149 100644 --- a/src/tree.jl +++ b/src/tree.jl @@ -30,5 +30,5 @@ AbstractTrees.ParentLinks(::Type{<:HwlocTreeNode}) = StoredParents() AbstractTrees.parent(n::HwlocTreeNode) = n.parent -AbstractTrees.NodeType(::Type{<:HwlocTreeNode}) where {T} = HasNodeType() -AbstractTrees.nodetype(::Type{<:HwlocTreeNode}) where {T} = HwlocTreeNode \ No newline at end of file +AbstractTrees.NodeType(::Type{<:HwlocTreeNode}) = HasNodeType() +AbstractTrees.nodetype(::Type{<:HwlocTreeNode}) = HwlocTreeNode \ No newline at end of file From 8d5d31c6011ed08ba6de2b04ceaf84664f39ed68 Mon Sep 17 00:00:00 2001 From: Johannes Blaschke Date: Wed, 26 Jul 2023 12:02:51 -0700 Subject: [PATCH 16/36] add taggable trees --- src/tree.jl | 77 +++++++++++++++++++++++++++++++++++++++++++---------- 1 file changed, 63 insertions(+), 14 deletions(-) diff --git a/src/tree.jl b/src/tree.jl index 2778149..ea78c62 100644 --- a/src/tree.jl +++ b/src/tree.jl @@ -1,21 +1,22 @@ import AbstractTrees -mutable struct HwlocTreeNode +mutable struct HwlocTreeNode{T} object::Object type::Symbol + tag::Union{Nothing, T} - parent::Union{Nothing, HwlocTreeNode} - children::Vector{HwlocTreeNode} - memory_children::Vector{HwlocTreeNode} - io_children::Vector{HwlocTreeNode} + parent::Union{Nothing, HwlocTreeNode{T}} + children::Vector{HwlocTreeNode{T}} + memory_children::Vector{HwlocTreeNode{T}} + io_children::Vector{HwlocTreeNode{T}} - function HwlocTreeNode(obj::Object; parent=nothing, type=nothing) - this = new(obj, obj.type_, parent) + function HwlocTreeNode{T}(obj::Object; parent=nothing, type=nothing) where {T} + this = new{T}(obj, obj.type_, nothing, parent) + + this.children = HwlocTreeNode{T}.(obj.children; parent=this) + this.memory_children = HwlocTreeNode{T}.(obj.memory_children; parent=this) + this.io_children = HwlocTreeNode{T}.(obj.io_children; parent=this) - this.children = HwlocTreeNode.(obj.children; parent=this) - this.memory_children = HwlocTreeNode.(obj.memory_children; parent=this) - this.io_children = HwlocTreeNode.(obj.io_children; parent=this) - return this end end @@ -26,9 +27,57 @@ end AbstractTrees.nodevalue(n::HwlocTreeNode) = n.object -AbstractTrees.ParentLinks(::Type{<:HwlocTreeNode}) = StoredParents() +AbstractTrees.ParentLinks(::Type{<:HwlocTreeNode}) = AbstractTrees.StoredParents() AbstractTrees.parent(n::HwlocTreeNode) = n.parent -AbstractTrees.NodeType(::Type{<:HwlocTreeNode}) = HasNodeType() -AbstractTrees.nodetype(::Type{<:HwlocTreeNode}) = HwlocTreeNode \ No newline at end of file +AbstractTrees.NodeType(::Type{<:HwlocTreeNode{T}}) where {T} = AbstractTrees.HasNodeType() +AbstractTrees.nodetype(::Type{<:HwlocTreeNode{T}}) where {T} = HwlocTreeNode{T} + +function AbstractTrees.printnode(io::IO, node::HwlocTreeNode) + obj = AbstractTrees.nodevalue(node) + label = string(obj) + if node.type in (:Package, :Core, :PU) + label = label * " [L#$(obj.logical_index) P#$(obj.os_index)]" + elseif node.type == :Bridge + if obj.attr.upstream_type == HWLOC_OBJ_BRIDGE_HOST + label = label * " [HostBridge]" + else + label = label * " [PCIBridge]" + end + elseif node.type == :PCI_Device + class_string = hwloc_pci_class_string(obj.attr.class_id) + label = label * " [" * @sprintf( + "%s%02x:%02x.%01x", + Char(obj.attr.domain), obj.attr.bus, obj.attr.dev, obj.attr.func + ) * " ($(class_string))]" + elseif node.type == :OS_Device + label = label * " [" * if obj.attr.type == HWLOC_OBJ_OSDEV_BLOCK + "Block$(subtype_str(obj))" + elseif obj.attr.type == HWLOC_OBJ_OSDEV_GPU + "GPU" + elseif obj.attr.type == HWLOC_OBJ_OSDEV_NETWORK + "Net" + elseif obj.attr.type == HWLOC_OBJ_OSDEV_OPENFABRICS + "OpenFabrics" + elseif obj.attr.type == HWLOC_OBJ_OSDEV_DMA + "DMA" + elseif obj.attr.type == HWLOC_OBJ_OSDEV_COPROC + "CoProc$(subtype_str(obj))" + else + string(obj.attr) + end * " \"$(obj.name)\"]" + end + print(io, label) +end + +get_nodes(tree_node, type) = filter( + x->x.type == type, + collect(AbstractTrees.PreOrderDFS(tree_node)) +) + +function tag_subtree!(tree_node, val) + for n in collect(AbstractTrees.PreOrderDFS(tree_node)) + n.tag = val + end +end From c8923d28fdd9d5174239986b0d419f44a10af5e9 Mon Sep 17 00:00:00 2001 From: Johannes Blaschke Date: Sat, 30 Dec 2023 21:31:30 -0800 Subject: [PATCH 17/36] update Pkg compat --- Project.toml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Project.toml b/Project.toml index c76d7d9..32cd262 100644 --- a/Project.toml +++ b/Project.toml @@ -10,9 +10,9 @@ Printf = "de0858da-6303-5e67-8744-51eddeeeb8d7" Statistics = "10745b16-79ce-11e8-11f9-7d13ad32a3b2" [compat] -CEnum = "0.4" +CEnum = "≥ 0.4" Hwloc_jll = "2.8" -julia = "1.6" +julia = "≥ 1.6" [extras] CpuId = "adafc99b-e345-5852-983c-f28acb93d879" From 0685838e430cbdea2c7fe17d3c4788c5294de5e5 Mon Sep 17 00:00:00 2001 From: Johannes Blaschke Date: Sat, 30 Dec 2023 21:49:08 -0800 Subject: [PATCH 18/36] add more control whether or not to print I/O devices --- src/highlevel_api.jl | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/src/highlevel_api.jl b/src/highlevel_api.jl index c180052..98a09d4 100644 --- a/src/highlevel_api.jl +++ b/src/highlevel_api.jl @@ -143,10 +143,10 @@ Returns the top-level system topology `Object`. On first call, it loads the topology by querying libhwloc and caches the result. Pass `reload=true` in order to force reload. """ -function gettopology(htopo=nothing; reload=false) +function gettopology(htopo=nothing; reload=false, get_io=true) if reload || (!isassigned(machine_topology)) if isnothing(htopo) - htopo=topology_init() + htopo=topology_init(;get_io=get_io) end machine_topology[] = topology_load(htopo) end @@ -391,7 +391,11 @@ The quality of the result might depend on the used terminal and might vary betwe **Note:** The specific visualization may change between minor versions. """ -function topology_graphical() - run(`$(lstopo_no_graphics()) --no-legend --of txt`) +function topology_graphical(;get_io=true) + if get_io + run(`$(lstopo_no_graphics()) --no-legend --of txt`) + else + run(`$(lstopo_no_graphics()) --no-io --no-legend --of txt`) + end return nothing end From 8fc464f8852d3b7dacd663cfb6798083a3e3bc76 Mon Sep 17 00:00:00 2001 From: Johannes Blaschke Date: Sat, 30 Dec 2023 21:59:38 -0800 Subject: [PATCH 19/36] Update docs --- src/highlevel_api.jl | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/src/highlevel_api.jl b/src/highlevel_api.jl index 98a09d4..9102f4b 100644 --- a/src/highlevel_api.jl +++ b/src/highlevel_api.jl @@ -47,9 +47,17 @@ function is_visible(obj::Object; minimal=true) end """ - print_topology([io::IO = stdout, obj::Object = gettopology()]) + print_topology( + io::IO = stdout, obj::Object = gettopology(); + indent = "", newline = false, prefix = "", minimal=true + ) + +Prints the topology of the given `obj` as a tree to `io`. -Prints the topology of the given `obj` as a tree to `io`. +**Note:** some systems have a great deal of extra PCI devices (think USB +bridges, and the many many device classes on custom systems like HPC clusters). +In order to mimmic the behaviour of the `lstopo` command, we ommit these devices +unless `minimal=false`. """ function print_topology( io::IO = stdout, obj::Object = gettopology(); From 694e62960d8d2f04962981bc3ce89e3d84f23480 Mon Sep 17 00:00:00 2001 From: Johannes Blaschke Date: Sat, 30 Dec 2023 22:04:10 -0800 Subject: [PATCH 20/36] fix how settings are filtered through --- src/highlevel_api.jl | 19 ++++++++++++++----- 1 file changed, 14 insertions(+), 5 deletions(-) diff --git a/src/highlevel_api.jl b/src/highlevel_api.jl index 9102f4b..fb0a34a 100644 --- a/src/highlevel_api.jl +++ b/src/highlevel_api.jl @@ -49,7 +49,7 @@ end """ print_topology( io::IO = stdout, obj::Object = gettopology(); - indent = "", newline = false, prefix = "", minimal=true + indent = "", newline = true, prefix = "", minimal=true ) Prints the topology of the given `obj` as a tree to `io`. @@ -61,7 +61,7 @@ unless `minimal=false`. """ function print_topology( io::IO = stdout, obj::Object = gettopology(); - indent = "", newline = false, prefix = "", minimal=true + indent = "", newline = true, prefix = "", minimal=true ) t = hwloc_typeof(obj) @@ -131,14 +131,23 @@ function print_topology( for child in obj.children no_newline = length(obj.children)==1 && t in (:L3Cache, :L2Cache, :L1Cache) if no_newline - print_topology(io, child; indent = indent, newline=false, prefix = " + ", ) + print_topology( + io, child; + indent = indent, newline=newline, prefix = " + ", minimal=minimal + ) else - print_topology(io, child; indent = indent*repeat(" ", 4), newline=true) + print_topology( + io, child; + indent = indent*repeat(" ", 4), newline=newline, minimal=minimal + ) end end for child in obj.io_children - print_topology(io, child; indent=indent*repeat(" ", 4), newline=true) + print_topology( + io, child; + indent=indent*repeat(" ", 4), newline=newline, minimal=minimal + ) end return nothing From 1d8e48c0b1e5c8d88aa8dfe733a753985d496b3b Mon Sep 17 00:00:00 2001 From: Johannes Blaschke Date: Sat, 30 Dec 2023 22:11:39 -0800 Subject: [PATCH 21/36] remove unnecessary import --- src/libhwloc_extensions.jl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libhwloc_extensions.jl b/src/libhwloc_extensions.jl index bb5d5b0..9a9984a 100644 --- a/src/libhwloc_extensions.jl +++ b/src/libhwloc_extensions.jl @@ -1,6 +1,6 @@ module LibHwlocExtensions -using ..LibHwloc: libhwloc, Hwloc_jll +using ..LibHwloc: libhwloc using CEnum From 22ad3188f087c3dee2dc77104ecec6b7234e2223 Mon Sep 17 00:00:00 2001 From: Johannes Blaschke Date: Sun, 31 Dec 2023 09:12:13 -0800 Subject: [PATCH 22/36] try unsafe_wrap instead of convert->copyto --- src/lowlevel_api.jl | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/src/lowlevel_api.jl b/src/lowlevel_api.jl index df2df55..8a056a8 100644 --- a/src/lowlevel_api.jl +++ b/src/lowlevel_api.jl @@ -277,11 +277,10 @@ function load(hobj::hwloc_obj_t) # topo.os_level = obj.os_level - obj_children = Vector{hwloc_obj_t}(UndefInitializer(), obj.arity) - obj_children_r = Base.unsafe_convert(Ptr{hwloc_obj_t}, obj_children) - unsafe_copyto!(obj_children_r, obj.children, obj.arity) - - children = Object[load(child) for child in obj_children] + children = Object[ + load(child) + for child in unsafe_wrap(Vector{hwloc_obj_t}, obj.children, obj.arity) + ] memory_children = Object[] if obj.memory_arity != 0 From 80c8e50b58d61ac228058a0c9f44403a46d7b41e Mon Sep 17 00:00:00 2001 From: Johannes Blaschke Date: Sun, 31 Dec 2023 13:09:57 -0800 Subject: [PATCH 23/36] make abstract tree interface an extension --- Project.toml | 14 ++++++++++++-- src/tree.jl => ext/HwlocTrees.jl | 12 ++++++++++-- src/Hwloc.jl | 2 -- 3 files changed, 22 insertions(+), 6 deletions(-) rename src/tree.jl => ext/HwlocTrees.jl (91%) diff --git a/Project.toml b/Project.toml index 017d43c..4f49935 100644 --- a/Project.toml +++ b/Project.toml @@ -4,20 +4,30 @@ authors = ["Erik Schnetter "] version = "3.1.1" [deps] -AbstractTrees = "1520ce14-60c1-5f80-bbc7-55ef81b5835c" CEnum = "fa961155-64e5-5f13-b03f-caf6b980ea82" Hwloc_jll = "e33a78d0-f292-5ffc-b300-72abe9b543c8" Printf = "de0858da-6303-5e67-8744-51eddeeeb8d7" Statistics = "10745b16-79ce-11e8-11f9-7d13ad32a3b2" +[weakdeps] +AbstractTrees = "1520ce14-60c1-5f80-bbc7-55ef81b5835c" + [compat] CEnum = "0.4" Hwloc_jll = "2.8" julia = "1.6" +AbstractTrees = "0.4.4" [extras] CpuId = "adafc99b-e345-5852-983c-f28acb93d879" Test = "8dfed614-e22c-5e08-85e1-65c5234f0b40" +AbstractTrees = "1520ce14-60c1-5f80-bbc7-55ef81b5835c" + +[extensions] +# * name of extension to the left +# * extension dependencies required to load the extension to the right +# * use a list for multiple extension dependencies +HwlocTrees = "AbstractTrees" [targets] -test = ["Test", "CpuId"] +test = ["Test", "CpuId", "AbstractTrees"] diff --git a/src/tree.jl b/ext/HwlocTrees.jl similarity index 91% rename from src/tree.jl rename to ext/HwlocTrees.jl index ea78c62..be5b22a 100644 --- a/src/tree.jl +++ b/ext/HwlocTrees.jl @@ -1,7 +1,10 @@ +module HwlocTrees + +using Hwloc, Printf import AbstractTrees mutable struct HwlocTreeNode{T} - object::Object + object::Hwloc.Object type::Symbol tag::Union{Nothing, T} @@ -10,7 +13,7 @@ mutable struct HwlocTreeNode{T} memory_children::Vector{HwlocTreeNode{T}} io_children::Vector{HwlocTreeNode{T}} - function HwlocTreeNode{T}(obj::Object; parent=nothing, type=nothing) where {T} + function HwlocTreeNode{T}(obj::Hwloc.Object; parent=nothing, type=nothing) where {T} this = new{T}(obj, obj.type_, nothing, parent) this.children = HwlocTreeNode{T}.(obj.children; parent=this) @@ -21,6 +24,10 @@ mutable struct HwlocTreeNode{T} end end +function AbstractTrees.children(node::Hwloc.Object) + HwlocTreeNode{UInt8}(node) +end + function AbstractTrees.children(node::HwlocTreeNode) tuple(node.children..., node.memory_children..., node.io_children...) end @@ -81,3 +88,4 @@ function tag_subtree!(tree_node, val) n.tag = val end end +end \ No newline at end of file diff --git a/src/Hwloc.jl b/src/Hwloc.jl index aff37da..1255d08 100644 --- a/src/Hwloc.jl +++ b/src/Hwloc.jl @@ -8,13 +8,11 @@ include("libhwloc.jl") include("libhwloc_extensions.jl") include("lowlevel_api.jl") include("highlevel_api.jl") -include("tree.jl") export topology, gettopology, topology_info, getinfo, print_topology, topology_graphical export num_physical_cores, num_virtual_cores, num_packages, num_numa_nodes export cachesize, cachelinesize export hwloc_typeof, hwloc_isa, collectobjects -export HwlocTreeNode const machine_topology = Ref{Object}() From 0236046619dacecd7da8504ea56c59307c49dcd5 Mon Sep 17 00:00:00 2001 From: Johannes Blaschke Date: Sun, 31 Dec 2023 13:19:52 -0800 Subject: [PATCH 24/36] get extension fully working --- ext/HwlocTrees.jl | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/ext/HwlocTrees.jl b/ext/HwlocTrees.jl index be5b22a..3e02f7c 100644 --- a/ext/HwlocTrees.jl +++ b/ext/HwlocTrees.jl @@ -47,29 +47,29 @@ function AbstractTrees.printnode(io::IO, node::HwlocTreeNode) if node.type in (:Package, :Core, :PU) label = label * " [L#$(obj.logical_index) P#$(obj.os_index)]" elseif node.type == :Bridge - if obj.attr.upstream_type == HWLOC_OBJ_BRIDGE_HOST + if obj.attr.upstream_type == Hwloc.LibHwloc.HWLOC_OBJ_BRIDGE_HOST label = label * " [HostBridge]" else label = label * " [PCIBridge]" end elseif node.type == :PCI_Device - class_string = hwloc_pci_class_string(obj.attr.class_id) + class_string = Hwloc.LibHwlocExtensions.hwloc_pci_class_string(obj.attr.class_id) label = label * " [" * @sprintf( "%s%02x:%02x.%01x", Char(obj.attr.domain), obj.attr.bus, obj.attr.dev, obj.attr.func ) * " ($(class_string))]" elseif node.type == :OS_Device - label = label * " [" * if obj.attr.type == HWLOC_OBJ_OSDEV_BLOCK - "Block$(subtype_str(obj))" - elseif obj.attr.type == HWLOC_OBJ_OSDEV_GPU + label = label * " [" * if obj.attr.type == Hwloc.LibHwloc.HWLOC_OBJ_OSDEV_BLOCK + "Block$(Hwloc.subtype_str(obj))" + elseif obj.attr.type == Hwloc.LibHwloc.HWLOC_OBJ_OSDEV_GPU "GPU" - elseif obj.attr.type == HWLOC_OBJ_OSDEV_NETWORK + elseif obj.attr.type == Hwloc.LibHwloc.HWLOC_OBJ_OSDEV_NETWORK "Net" - elseif obj.attr.type == HWLOC_OBJ_OSDEV_OPENFABRICS + elseif obj.attr.type == Hwloc.LibHwloc.HWLOC_OBJ_OSDEV_OPENFABRICS "OpenFabrics" - elseif obj.attr.type == HWLOC_OBJ_OSDEV_DMA + elseif obj.attr.type == Hwloc.LibHwloc.HWLOC_OBJ_OSDEV_DMA "DMA" - elseif obj.attr.type == HWLOC_OBJ_OSDEV_COPROC + elseif obj.attr.type == Hwloc.LibHwloc.HWLOC_OBJ_OSDEV_COPROC "CoProc$(subtype_str(obj))" else string(obj.attr) From 9f0253bfbb4a2fe2ff23bdbd47132e90182f8e9c Mon Sep 17 00:00:00 2001 From: Johannes Blaschke Date: Sun, 31 Dec 2023 13:39:53 -0800 Subject: [PATCH 25/36] add tests --- test/runtests.jl | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/test/runtests.jl b/test/runtests.jl index 241a0e4..54b5824 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -119,4 +119,11 @@ import CpuId @test first(collectobjects(:NUMANode)).mem > 0 end end + + @testset "AbstractTrees interface" begin + using AbstractTrees + t = gettopology() + # check that `children(gettopology)` returns the root of the HwlocTree + @test children(t).type == :Machine + end end From 7382bf4992c3bb8679898fc26b8d55d068074f0b Mon Sep 17 00:00:00 2001 From: Johannes Blaschke Date: Sun, 31 Dec 2023 15:44:53 -0800 Subject: [PATCH 26/36] update readme --- README.md | 161 ++++++++++++++++++++++++++++++++++---------- src/lowlevel_api.jl | 8 ++- 2 files changed, 133 insertions(+), 36 deletions(-) diff --git a/README.md b/README.md index 69aeb33..6e038e1 100644 --- a/README.md +++ b/README.md @@ -24,28 +24,45 @@ On my laptop this gives the following output: julia> using Hwloc julia> topology() -Machine (16.0 GB) - Package L#0 P#0 (16.0 GB) - NUMANode (16.0 GB) + +Machine (31.05 GB) + Package L#0 P#0 (31.05 GB) + NUMANode (31.05 GB) L3 (12.0 MB) - L2 (256.0 kB) + L1 (32.0 kB) + Core L#0 P#0 + L2 (1.25 MB) + + L1 (48.0 kB) + + Core L#0 P#0 PU L#0 P#0 - PU L#1 P#1 - L2 (256.0 kB) + L1 (32.0 kB) + Core L#1 P#1 - PU L#2 P#2 - PU L#3 P#3 - L2 (256.0 kB) + L1 (32.0 kB) + Core L#2 P#2 - PU L#4 P#4 - PU L#5 P#5 - L2 (256.0 kB) + L1 (32.0 kB) + Core L#3 P#3 - PU L#6 P#6 + PU L#1 P#4 + L2 (1.25 MB) + + L1 (48.0 kB) + + Core L#1 P#1 + PU L#2 P#1 + PU L#3 P#5 + L2 (1.25 MB) + + L1 (48.0 kB) + + Core L#2 P#2 + PU L#4 P#2 + PU L#5 P#6 + L2 (1.25 MB) + + L1 (48.0 kB) + + Core L#3 P#3 + PU L#6 P#3 PU L#7 P#7 - L2 (256.0 kB) + L1 (32.0 kB) + Core L#4 P#4 - PU L#8 P#8 - PU L#9 P#9 - L2 (256.0 kB) + L1 (32.0 kB) + Core L#5 P#5 - PU L#10 P#10 - PU L#11 P#11 + HostBridge + PCI 00:02.0 (VGA) + GPU "renderD128" + GPU "card0" + PCIBridge + PCI 01:00.0 (NVMExp) + Block(Disk) "nvme0n1" + PCIBridge + PCI 72:00.0 (Network) + Net "wlp114s0" + PCIBridge + PCI 73:00.0 (Other) + Block "mmcblk0" + ``` Often, one is only interested in a summary of this topology. @@ -53,14 +70,14 @@ The function `topology_info()` provides such a compact description, which is loo ```julia julia> topology_info() -Machine: 1 (16.0 GB) - Package: 1 (16.0 GB) - NUMANode: 1 (16.0 GB) +Machine: 1 (31.05 GB) + Package: 1 (31.05 GB) + NUMANode: 1 (31.05 GB) L3Cache: 1 (12.0 MB) - L2Cache: 6 (256.0 KB) - L1Cache: 6 (32.0 KB) - Core: 6 - PU: 12 + L2Cache: 4 (1.25 MB) + L1Cache: 4 (48.0 kB) + Core: 4 + PU: 8 ``` If you prefer a more verbose graphical visualization you may consider using `topology_graphical()`: @@ -95,14 +112,14 @@ One may also use `getinfo()` to programmatically access some of the output of `t ```julia julia> getinfo() -Dict{Symbol,Int64} with 8 entries: - :L2Cache => 6 +Dict{Symbol, Int64} with 8 entries: + :L2Cache => 4 :NUMANode => 1 - :Core => 6 + :Core => 4 :Package => 1 - :L1Cache => 6 + :L1Cache => 4 :Machine => 1 - :PU => 12 + :PU => 8 :L3Cache => 1 ``` @@ -190,10 +207,84 @@ julia> collectobjects(:PU, l2cache) Hwloc.Object: PU ``` -### Manual topology query +### Manual topology query and caching On the first call of `gettopology()`, Hwloc.jl examines the current machine's hardware topology and caches the result in `Hwloc.machine_topology`. -To manually query the system topology one may use `Hwloc.topology_load` -which directly `ccall`s into `libhwloc` and directly returns the -resulting `Hwloc.Object`. + +To query the system the system topology again -- i.e. not using the cached +`Hwloc.Object` representing the entire machine -- simply pass the `reload=true` (`false` by default) kwarg: + +```julia +julia> topo = gettopology(;reload=true) +Hwloc.Object: Machine +``` + +### Do not include I/O devices in topology object + +You may prefer not to include I/O devices in you Hwloc tree, then we recommend +passing the `get_io=false` (`true` by default) kwarg, in addition to `reload` +(cf. above): + +```julia +julia> topo = gettopology(;reload=true, get_io=false) +Hwloc.Object: Machine + +julia> topology(topo) +Machine (31.05 GB) + Package L#0 P#0 (31.05 GB) + NUMANode (31.05 GB) + L3 (12.0 MB) + L2 (1.25 MB) + L1 (48.0 kB) + Core L#0 P#0 + PU L#0 P#0 + PU L#1 P#4 + L2 (1.25 MB) + L1 (48.0 kB) + Core L#1 P#1 + PU L#2 P#1 + PU L#3 P#5 + L2 (1.25 MB) + L1 (48.0 kB) + Core L#2 P#2 + PU L#4 P#2 + PU L#5 P#6 + L2 (1.25 MB) + L1 (48.0 kB) + Core L#3 P#3 + PU L#6 P#3 + PU L#7 P#7 +``` +(note: to avoid caching by eccident, we recommend passing `reload=true` to +`gettopology`) + +### Low-level API for accessing the underlying topology object. + +**Warning:** As discussed earlier, `Hwloc.jl` makes heavy use of caching in the +high-level API. Using the low-level and high-level APIs together can result in +cached values being used by accident! We therefore recommend that the high-level +`gettopology` funcion is used, where caching is controlled via the `reload` +kwarg. + +Under the hood, `gettopology` uses `Hwloc.topology_init` and +`Hwloc.topology_load` to directly `ccall` into `libhwloc`. `Hwloc.topology_init` +is reponsible for creating a low-level `LibHwloc.hwloc_topology` object. +`Hwloc.topology_load` wraps this a `Hwloc.Object` Julia object. + +**Note:** `Hwloc.topology_load` is destructive to the `LibHwloc.hwloc_topology` +object: + +```julia +julia> htopo = Hwloc.topology_init() +Ptr{Hwloc.LibHwloc.hwloc_topology} @0x000000000883cf60 + +julia> topo = Hwloc.topology_load(htopo) +Hwloc.Object: Machine + +julia> topo = Hwloc.topology_load(htopo) +ERROR: AssertionError: ierr == 0 +Stacktrace: + [1] topology_load(htopo::Ptr{Hwloc.LibHwloc.hwloc_topology}) + @ Hwloc ~/.julia/dev/Hwloc/src/lowlevel_api.jl:347 + [2] top-level scope + @ REPL[78]:1 +``` + +This is because `LibHwloc.hwloc_topology` are not garbage-collected (a call to +`Hwloc.topology_init`, without a later call to `Hwloc.hwloc_topology_destroy` +will leak memory). This is why `Hwloc.topology_load` calls +`Hwloc.hwloc_topology_destroy` after creating the `Hwloc.Object` Julia object +(which is garbage collected!). \ No newline at end of file diff --git a/src/lowlevel_api.jl b/src/lowlevel_api.jl index 8a056a8..8140654 100644 --- a/src/lowlevel_api.jl +++ b/src/lowlevel_api.jl @@ -317,6 +317,12 @@ function load(hobj::hwloc_obj_t) end +""" + topology_init(;get_io=true) + +Init underlying Hwloc objec, and set the type filter to +HWLOC_TYPE_FILTER_KEEP_ALL if `get_io==true` +""" function topology_init(;get_io=true) r_htopo = Ref{hwloc_topology_t}() hwloc_topology_init(r_htopo) @@ -356,4 +362,4 @@ function topology_load(htopo=topology_init()) hwloc_topology_destroy(htopo) return topo -end +end \ No newline at end of file From 372fc6af8faefd62319f0c7d53dfe0557675c8b4 Mon Sep 17 00:00:00 2001 From: Johannes Blaschke Date: Sun, 31 Dec 2023 15:53:42 -0800 Subject: [PATCH 27/36] compatibility with older versions of Julia --- Project.toml | 1 + src/Hwloc.jl | 5 +++++ 2 files changed, 6 insertions(+) diff --git a/Project.toml b/Project.toml index 6eddd9d..61b5e0f 100644 --- a/Project.toml +++ b/Project.toml @@ -8,6 +8,7 @@ CEnum = "fa961155-64e5-5f13-b03f-caf6b980ea82" Hwloc_jll = "e33a78d0-f292-5ffc-b300-72abe9b543c8" Printf = "de0858da-6303-5e67-8744-51eddeeeb8d7" Statistics = "10745b16-79ce-11e8-11f9-7d13ad32a3b2" +AbstractTrees = "1520ce14-60c1-5f80-bbc7-55ef81b5835c" [weakdeps] AbstractTrees = "1520ce14-60c1-5f80-bbc7-55ef81b5835c" diff --git a/src/Hwloc.jl b/src/Hwloc.jl index 1255d08..6911f5e 100644 --- a/src/Hwloc.jl +++ b/src/Hwloc.jl @@ -16,4 +16,9 @@ export hwloc_typeof, hwloc_isa, collectobjects const machine_topology = Ref{Object}() +# Compatibility with older Julia versions + module extensions: +if !isdefined(Base, :get_extension) + include(joinpath("..", "ext", "HwlocTrees.jl")) +end + end From 3e020d958b992f94e06a2c8f4770c4e9065b7bb9 Mon Sep 17 00:00:00 2001 From: Johannes Blaschke Date: Sun, 31 Dec 2023 16:18:22 -0800 Subject: [PATCH 28/36] update readme --- README.md | 70 +++++++++++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 68 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 6e038e1..525d85e 100644 --- a/README.md +++ b/README.md @@ -123,7 +123,6 @@ Dict{Symbol, Int64} with 8 entries: :L3Cache => 1 ``` - ### Cache properties Assuming that multiple caches of the same level (e.g. L1) have identical properties, one can use the convenience functions `cachesize()` and `cachelinesize()` to obtain the relevant sizes in Bytes: @@ -287,4 +286,71 @@ This is because `LibHwloc.hwloc_topology` are not garbage-collected (a call to `Hwloc.topology_init`, without a later call to `Hwloc.hwloc_topology_destroy` will leak memory). This is why `Hwloc.topology_load` calls `Hwloc.hwloc_topology_destroy` after creating the `Hwloc.Object` Julia object -(which is garbage collected!). \ No newline at end of file +(which is garbage collected!). + + +## Hwloc objects are `AbstractTrees` + +If the [`AbstractTrees`](https://github.com/JuliaCollections/AbstractTrees.jl) +module is loaded, then passing an `Hwloc.Object` to `AbstractTrees.children` +will construct an `HwlocTreeNode`. Calling `children(gettopology())` will +return the Hwloc tree root: + +```julia +julia> using AbstractTrees, Hwloc + +julia> t = children(gettopology()); + +julia> print_tree(t; maxdepth=2) +Hwloc.Object: Machine +├─ Hwloc.Object: Package [L#0 P#0] +│ ├─ Hwloc.Object: L3Cache +│ │ ⋮ +│ │ +│ └─ Hwloc.Object: NUMANode +└─ Hwloc.Object: Bridge [HostBridge] + ├─ Hwloc.Object: PCI_Device [00:00.0 (HostBridge)] + ├─ Hwloc.Object: PCI_Device [00:02.0 (VGA)] + │ ⋮ + │ + ├─ Hwloc.Object: PCI_Device [00:04.0 (SignalProcessing)] + ├─ Hwloc.Object: Bridge [PCIBridge] + │ ⋮ + │ + ├─ Hwloc.Object: Bridge [PCIBridge] + ├─ Hwloc.Object: Bridge [PCIBridge] + ├─ Hwloc.Object: PCI_Device [00:0a.0 (SignalProcessing)] + ├─ Hwloc.Object: PCI_Device [00:0d.0 (USB)] + ├─ Hwloc.Object: PCI_Device [00:0d.2 (USB)] + ├─ Hwloc.Object: PCI_Device [00:0d.3 (USB)] + ├─ Hwloc.Object: PCI_Device [00:12.0 (Serial)] + ├─ Hwloc.Object: PCI_Device [00:14.0 (USB)] + ├─ Hwloc.Object: PCI_Device [00:14.2 (RAM)] + ├─ Hwloc.Object: PCI_Device [00:15.0 (SerialBus)] + │ ⋮ + │ + ├─ Hwloc.Object: PCI_Device [00:15.1 (SerialBus)] + │ ⋮ + │ + ├─ Hwloc.Object: PCI_Device [00:16.0 (Communication)] + ├─ Hwloc.Object: PCI_Device [00:19.0 (SerialBus)] + │ ⋮ + │ + ├─ Hwloc.Object: PCI_Device [00:19.1 (SerialBus)] + │ ⋮ + │ + ├─ Hwloc.Object: Bridge [PCIBridge] + │ ⋮ + │ + ├─ Hwloc.Object: Bridge [PCIBridge] + │ ⋮ + │ + ├─ Hwloc.Object: PCI_Device [00:1f.0 (ISABridge)] + ├─ Hwloc.Object: PCI_Device [00:1f.3 (MultimediaAudio)] + ├─ Hwloc.Object: PCI_Device [00:1f.4 (SMBus)] + └─ Hwloc.Object: PCI_Device [00:1f.5 (SerialBus)] + +``` + +For examples of using the AbstracTree interface to search the Hwloc tree, see: +[NetworkInterfaceControllers.jl](https://github.com/JuliaParallel/NetworkInterfaceControllers.jl) \ No newline at end of file From e016f4327efb92ccd070655d38d62dc1230a4a5d Mon Sep 17 00:00:00 2001 From: Johannes Blaschke Date: Sat, 6 Jan 2024 10:47:02 -0800 Subject: [PATCH 29/36] oops! fixed it --- src/highlevel_api.jl | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/highlevel_api.jl b/src/highlevel_api.jl index fb0a34a..9e9f6b7 100644 --- a/src/highlevel_api.jl +++ b/src/highlevel_api.jl @@ -133,12 +133,12 @@ function print_topology( if no_newline print_topology( io, child; - indent = indent, newline=newline, prefix = " + ", minimal=minimal + indent = indent, newline=false, prefix = " + ", minimal=minimal ) else print_topology( io, child; - indent = indent*repeat(" ", 4), newline=newline, minimal=minimal + indent = indent*repeat(" ", 4), newline=true, minimal=minimal ) end end @@ -146,7 +146,7 @@ function print_topology( for child in obj.io_children print_topology( io, child; - indent=indent*repeat(" ", 4), newline=newline, minimal=minimal + indent=indent*repeat(" ", 4), newline=true, minimal=minimal ) end From dcfe3ba369e597d4164abdd03e182b1fd04d6365 Mon Sep 17 00:00:00 2001 From: Johannes Blaschke Date: Sat, 6 Jan 2024 11:08:14 -0800 Subject: [PATCH 30/36] get_io -> io --- README.md | 6 +++--- src/highlevel_api.jl | 8 ++++---- src/lowlevel_api.jl | 8 ++++---- 3 files changed, 11 insertions(+), 11 deletions(-) diff --git a/README.md b/README.md index 6e038e1..f2cbf38 100644 --- a/README.md +++ b/README.md @@ -223,11 +223,11 @@ Hwloc.Object: Machine ### Do not include I/O devices in topology object You may prefer not to include I/O devices in you Hwloc tree, then we recommend -passing the `get_io=false` (`true` by default) kwarg, in addition to `reload` -(cf. above): +passing the `io=false` (`true` by default) kwarg, in addition to `reload` (cf. +above): ```julia -julia> topo = gettopology(;reload=true, get_io=false) +julia> topo = gettopology(;reload=true, io=false) Hwloc.Object: Machine julia> topology(topo) diff --git a/src/highlevel_api.jl b/src/highlevel_api.jl index 9e9f6b7..788acdc 100644 --- a/src/highlevel_api.jl +++ b/src/highlevel_api.jl @@ -160,10 +160,10 @@ Returns the top-level system topology `Object`. On first call, it loads the topology by querying libhwloc and caches the result. Pass `reload=true` in order to force reload. """ -function gettopology(htopo=nothing; reload=false, get_io=true) +function gettopology(htopo=nothing; reload=false, io=true) if reload || (!isassigned(machine_topology)) if isnothing(htopo) - htopo=topology_init(;get_io=get_io) + htopo=topology_init(;io=io) end machine_topology[] = topology_load(htopo) end @@ -408,8 +408,8 @@ The quality of the result might depend on the used terminal and might vary betwe **Note:** The specific visualization may change between minor versions. """ -function topology_graphical(;get_io=true) - if get_io +function topology_graphical(;io=true) + if io run(`$(lstopo_no_graphics()) --no-legend --of txt`) else run(`$(lstopo_no_graphics()) --no-io --no-legend --of txt`) diff --git a/src/lowlevel_api.jl b/src/lowlevel_api.jl index 8140654..63d9848 100644 --- a/src/lowlevel_api.jl +++ b/src/lowlevel_api.jl @@ -318,15 +318,15 @@ end """ - topology_init(;get_io=true) + topology_init(;io=true) Init underlying Hwloc objec, and set the type filter to -HWLOC_TYPE_FILTER_KEEP_ALL if `get_io==true` +HWLOC_TYPE_FILTER_KEEP_ALL if `io==true` """ -function topology_init(;get_io=true) +function topology_init(;io=true) r_htopo = Ref{hwloc_topology_t}() hwloc_topology_init(r_htopo) - if get_io + if io hwloc_topology_set_io_types_filter( r_htopo[], LibHwloc.HWLOC_TYPE_FILTER_KEEP_ALL ) From 3c6e5afedf75fefc701d1b675d46bffe2ebd3a99 Mon Sep 17 00:00:00 2001 From: Johannes Blaschke Date: Sat, 6 Jan 2024 15:24:15 -0800 Subject: [PATCH 31/36] Add iterator over I/O devices --- src/lowlevel_api.jl | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/lowlevel_api.jl b/src/lowlevel_api.jl index 63d9848..b190725 100644 --- a/src/lowlevel_api.jl +++ b/src/lowlevel_api.jl @@ -238,7 +238,10 @@ IteratorSize(::Type{Object}) = Base.SizeUnknown() IteratorEltype(::Type{Object}) = Base.HasEltype() eltype(::Type{Object}) = Object isempty(::Object) = false -iterate(obj::Object) = (obj, isempty(obj.memory_children) ? obj.children : vcat(obj.memory_children, obj.children)) +function iterate(obj::Object) + state = vcat(obj.children, obj.memory_children, obj.io_children) + return obj, state +end function iterate(::Object, state::Vector{Object}) isempty(state) && return nothing # depth-first traversal @@ -246,6 +249,7 @@ function iterate(::Object, state::Vector{Object}) obj, state = state[1], state[2:end] prepend!(state, obj.children) prepend!(state, obj.memory_children) + prepend!(state, obj.io_children) return obj, state end # length(obj::Object) = mapreduce(x->1, +, obj) From 9b529e937d7aabe866b3409e6f63c48367ab0e91 Mon Sep 17 00:00:00 2001 From: Johannes Blaschke Date: Sat, 6 Jan 2024 15:53:16 -0800 Subject: [PATCH 32/36] Add all devices to getinfo --- src/highlevel_api.jl | 24 ++++++++++++++---------- test/runtests.jl | 4 ++-- 2 files changed, 16 insertions(+), 12 deletions(-) diff --git a/src/highlevel_api.jl b/src/highlevel_api.jl index 788acdc..159f796 100644 --- a/src/highlevel_api.jl +++ b/src/highlevel_api.jl @@ -174,7 +174,7 @@ end """ Prints the system topology as a tree. """ -topology(htopo=nothing) = print_topology(gettopology(htopo)) +topology(topo=gettopology()) = print_topology(topo) """ Prints a summary of the system topology (loosely similar to `hwloc-info`). @@ -200,21 +200,25 @@ function topology_info() end """ + getinfo(topo=gettopology(); list_all=false) + Programmatic version of `topology_info()`. Returns a `Dict{Symbol,Int}` whose entries indicate which and how often certain hwloc elements are present. -If the keyword argument `list_all` (default: `false`) is set to `true`, -the resulting dictionary will contain all possible hwloc elements. +If the `list_all` kwarg is `true`, then the results Dict will have a key for +each Hwloc type. **Warning:** a zero count does not necessarily mean that such +a device is not present -- e.g. the following +``` +getinfo(gettopology(;reload=true, io=false); list_all=true) +``` +will show a `PCI_Device` count of zero, even though those devices are present +(the zero count is due to the `io=false` kwarg passed to `gettopology`). """ -function getinfo(; list_all::Bool = false) +function getinfo(topo=gettopology(); list_all=false) res = list_all ? Dict{Symbol,Int}(t => 0 for t in obj_types) : Dict{Symbol, Int}() - for subobj in gettopology() + for subobj in topo t = hwloc_typeof(subobj) - if t in keys(res) - res[t] += 1 - else - res[t] = 1 - end + res[t] = get!(res, t, 0) + 1 end return res end diff --git a/test/runtests.jl b/test/runtests.jl index 241a0e4..042d326 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -23,7 +23,7 @@ import CpuId @testset "Topology (compact info)" begin println("Info:") topology_info() - counts = getinfo(list_all=true) + counts = getinfo(;list_all=true) @test typeof(counts) == Dict{Symbol,Int} @test length(counts) == length(Hwloc.obj_types) println(counts) @@ -34,7 +34,7 @@ import CpuId @test num_virtual_cores() == counts[:PU] @test num_packages() == counts[:Package] @test num_numa_nodes() == counts[:NUMANode] - counts = getinfo(list_all=false) + counts = getinfo() @test typeof(counts) == Dict{Symbol,Int} @test all(>(0), values(counts)) end From 7e4bd47615fd84c55a0f1a81fc3ebabbd80c8faf Mon Sep 17 00:00:00 2001 From: Johannes Blaschke Date: Sat, 6 Jan 2024 15:57:05 -0800 Subject: [PATCH 33/36] Add PCI devices to topology_info --- README.md | 24 +++++++++++++++--------- src/highlevel_api.jl | 6 ++++-- 2 files changed, 19 insertions(+), 11 deletions(-) diff --git a/README.md b/README.md index f2cbf38..c0d9cc3 100644 --- a/README.md +++ b/README.md @@ -78,6 +78,9 @@ Machine: 1 (31.05 GB) L1Cache: 4 (48.0 kB) Core: 4 PU: 8 + Bridge: 6 + PCI_Device: 22 + OS_Device: 13 ``` If you prefer a more verbose graphical visualization you may consider using `topology_graphical()`: @@ -112,15 +115,18 @@ One may also use `getinfo()` to programmatically access some of the output of `t ```julia julia> getinfo() -Dict{Symbol, Int64} with 8 entries: - :L2Cache => 4 - :NUMANode => 1 - :Core => 4 - :Package => 1 - :L1Cache => 4 - :Machine => 1 - :PU => 8 - :L3Cache => 1 +Dict{Symbol, Int64} with 11 entries: + :Package => 1 + :PU => 8 + :OS_Device => 13 + :Core => 4 + :L3Cache => 1 + :Machine => 1 + :PCI_Device => 22 + :L2Cache => 4 + :NUMANode => 1 + :Bridge => 6 + :L1Cache => 4 ``` diff --git a/src/highlevel_api.jl b/src/highlevel_api.jl index 159f796..371ec38 100644 --- a/src/highlevel_api.jl +++ b/src/highlevel_api.jl @@ -177,11 +177,13 @@ Prints the system topology as a tree. topology(topo=gettopology()) = print_topology(topo) """ + topology_info(topo=gettopology()) + Prints a summary of the system topology (loosely similar to `hwloc-info`). """ -function topology_info() +function topology_info(topo=gettopology()) nodes = Tuple{Symbol, Int64, String}[] - for subobj in gettopology() + for subobj in topo idx = findfirst(t->t[1] == subobj.type_, nodes) if isnothing(idx) attrstr = "" From 90afda0ba75ea64b146bd7cb18bee3eb4b7dc936 Mon Sep 17 00:00:00 2001 From: Johannes Blaschke Date: Sat, 6 Jan 2024 16:00:18 -0800 Subject: [PATCH 34/36] Update topology_graphical() --- README.md | 49 ++++++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 46 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index c0d9cc3..67f1f4c 100644 --- a/README.md +++ b/README.md @@ -85,9 +85,52 @@ Machine: 1 (31.05 GB) If you prefer a more verbose graphical visualization you may consider using `topology_graphical()`: -Screenshot 2022-09-27 at 12 06 57 - -(Note that as of now this may not produce colorful output on all systems.) +``` +julia> topology_graphical() +┌───────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐ +│ Machine (31GB total) │ +│ │ +│ ┌────────────────────────────────────────────────────────────────────┐ ├┤╶─┬─────┬─────────────┐ │ +│ │ Package L#0 │ │ │ PCI 00:02.0 │ │ +│ │ │ │ └─────────────┘ │ +│ │ ┌────────────────────────────────────────────────────────────────┐ │ │ │ +│ │ │ NUMANode L#0 P#0 (31GB) │ │ ├─────┼┤╶───────┬───────────────────┐ │ +│ │ └────────────────────────────────────────────────────────────────┘ │ │3.9 3.9 │ PCI 01:00.0 │ │ +│ │ │ │ │ │ │ +│ │ ┌────────────────────────────────────────────────────────────────┐ │ │ │ ┌───────────────┐ │ │ +│ │ │ L3 (12MB) │ │ │ │ │ Block nvme0n1 │ │ │ +│ │ └────────────────────────────────────────────────────────────────┘ │ │ │ │ │ │ │ +│ │ │ │ │ │ 953 GB │ │ │ +│ │ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ │ │ │ └───────────────┘ │ │ +│ │ │ L2 (1280KB) │ │ L2 (1280KB) │ │ L2 (1280KB) │ │ L2 (1280KB) │ │ │ └───────────────────┘ │ +│ │ └─────────────┘ └─────────────┘ └─────────────┘ └─────────────┘ │ │ │ +│ │ │ ├─────┼┤╶───────┬──────────────────┐ │ +│ │ ┌────────────┐ ┌────────────┐ ┌────────────┐ ┌────────────┐ │ │0.6 0.6 │ PCI 72:00.0 │ │ +│ │ │ L1d (48KB) │ │ L1d (48KB) │ │ L1d (48KB) │ │ L1d (48KB) │ │ │ │ │ │ +│ │ └────────────┘ └────────────┘ └────────────┘ └────────────┘ │ │ │ ┌──────────────┐ │ │ +│ │ │ │ │ │ Net wlp114s0 │ │ │ +│ │ ┌────────────┐ ┌────────────┐ ┌────────────┐ ┌────────────┐ │ │ │ └──────────────┘ │ │ +│ │ │ L1i (32KB) │ │ L1i (32KB) │ │ L1i (32KB) │ │ L1i (32KB) │ │ │ └──────────────────┘ │ +│ │ └────────────┘ └────────────┘ └────────────┘ └────────────┘ │ │ │ +│ │ │ └─────┼┤╶───────┬───────────────┐ │ +│ │ ┌────────────┐ ┌────────────┐ ┌────────────┐ ┌────────────┐ │ 1.0 │ Block mmcblk0 │ │ +│ │ │ Core L#0 │ │ Core L#1 │ │ Core L#2 │ │ Core L#3 │ │ │ │ │ +│ │ │ │ │ │ │ │ │ │ │ │ 238 GB │ │ +│ │ │ ┌────────┐ │ │ ┌────────┐ │ │ ┌────────┐ │ │ ┌────────┐ │ │ └───────────────┘ │ +│ │ │ │ PU L#0 │ │ │ │ PU L#2 │ │ │ │ PU L#4 │ │ │ │ PU L#6 │ │ │ │ +│ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ +│ │ │ │ P#0 │ │ │ │ P#1 │ │ │ │ P#2 │ │ │ │ P#3 │ │ │ │ +│ │ │ └────────┘ │ │ └────────┘ │ │ └────────┘ │ │ └────────┘ │ │ │ +│ │ │ ┌────────┐ │ │ ┌────────┐ │ │ ┌────────┐ │ │ ┌────────┐ │ │ │ +│ │ │ │ PU L#1 │ │ │ │ PU L#3 │ │ │ │ PU L#5 │ │ │ │ PU L#7 │ │ │ │ +│ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ +│ │ │ │ P#4 │ │ │ │ P#5 │ │ │ │ P#6 │ │ │ │ P#7 │ │ │ │ +│ │ │ └────────┘ │ │ └────────┘ │ │ └────────┘ │ │ └────────┘ │ │ │ +│ │ └────────────┘ └────────────┘ └────────────┘ └────────────┘ │ │ +│ └────────────────────────────────────────────────────────────────────┘ │ +└───────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘ +``` +(Note that as of now this may produce colorful output on some systems.) ## Obtaining particular information: From 1f2135b80e4e044b227321f25b872862c2d554e9 Mon Sep 17 00:00:00 2001 From: Johannes Blaschke Date: Sat, 6 Jan 2024 16:07:59 -0800 Subject: [PATCH 35/36] Update README to reflect changes to print_topology --- README.md | 16 ++++------------ 1 file changed, 4 insertions(+), 12 deletions(-) diff --git a/README.md b/README.md index 67f1f4c..8fab81b 100644 --- a/README.md +++ b/README.md @@ -29,24 +29,16 @@ Machine (31.05 GB) Package L#0 P#0 (31.05 GB) NUMANode (31.05 GB) L3 (12.0 MB) - L2 (1.25 MB) - + L1 (48.0 kB) - + Core L#0 P#0 + L2 (1.25 MB) + L1 (48.0 kB) + Core L#0 P#0 PU L#0 P#0 PU L#1 P#4 - L2 (1.25 MB) - + L1 (48.0 kB) - + Core L#1 P#1 + L2 (1.25 MB) + L1 (48.0 kB) + Core L#1 P#1 PU L#2 P#1 PU L#3 P#5 - L2 (1.25 MB) - + L1 (48.0 kB) - + Core L#2 P#2 + L2 (1.25 MB) + L1 (48.0 kB) + Core L#2 P#2 PU L#4 P#2 PU L#5 P#6 - L2 (1.25 MB) - + L1 (48.0 kB) - + Core L#3 P#3 + L2 (1.25 MB) + L1 (48.0 kB) + Core L#3 P#3 PU L#6 P#3 PU L#7 P#7 HostBridge From 6d4c0e24b1374acca9d2db74cbc4ef832e84535f Mon Sep 17 00:00:00 2001 From: Johannes Blaschke Date: Sat, 6 Jan 2024 16:09:21 -0800 Subject: [PATCH 36/36] typo --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 4fde833..b3d2719 100644 --- a/README.md +++ b/README.md @@ -288,7 +288,7 @@ Machine (31.05 GB) PU L#6 P#3 PU L#7 P#7 ``` -(note: to avoid caching by eccident, we recommend passing `reload=true` to +(note: to avoid caching by accident, we recommend passing `reload=true` to `gettopology`) ### Low-level API for accessing the underlying topology object.