Files
gonum/graph/iterator/nodes_map_safe.go
Dan Kortschak 2bef024c93 graph/iterator: improve reflect-based iterators
- use SetIterValue to reduce allocations
- use explicit stored iterator length

goos: linux
goarch: amd64
pkg: gonum.org/v1/gonum/graph/traverse
cpu: Intel(R) Core(TM) i7-6700HQ CPU @ 2.60GHz
                                    │  old.bench  │              new.bench              │
                                    │   sec/op    │   sec/op     vs base                │
WalkAllBreadthFirstGnp_10_tenth-8     3.575µ ± 2%   2.961µ ± 2%  -17.18% (p=0.000 n=20)
WalkAllBreadthFirstGnp_100_tenth-8    144.4µ ± 1%   132.4µ ± 1%   -8.33% (p=0.000 n=20)
WalkAllBreadthFirstGnp_1000_tenth-8   12.66m ± 2%   12.00m ± 2%   -5.20% (p=0.000 n=20)
WalkAllBreadthFirstGnp_10_half-8      8.415µ ± 1%   7.615µ ± 1%   -9.51% (p=0.000 n=20)
WalkAllBreadthFirstGnp_100_half-8     628.0µ ± 1%   580.7µ ± 1%   -7.54% (p=0.000 n=20)
WalkAllBreadthFirstGnp_1000_half-8    58.74m ± 1%   55.79m ± 2%   -5.03% (p=0.000 n=20)
WalkAllDepthFirstGnp_10_tenth-8       3.539µ ± 2%   2.956µ ± 1%  -16.49% (p=0.000 n=20)
WalkAllDepthFirstGnp_100_tenth-8      144.8µ ± 2%   135.5µ ± 1%   -6.40% (p=0.000 n=20)
WalkAllDepthFirstGnp_1000_tenth-8     12.40m ± 1%   12.02m ± 1%   -3.10% (p=0.000 n=20)
WalkAllDepthFirstGnp_10_half-8        8.210µ ± 1%   7.423µ ± 1%   -9.59% (p=0.000 n=20)
WalkAllDepthFirstGnp_100_half-8       625.8µ ± 1%   598.3µ ± 1%   -4.40% (p=0.000 n=20)
WalkAllDepthFirstGnp_1000_half-8      58.28m ± 1%   55.65m ± 1%   -4.52% (p=0.000 n=20)
geomean                               353.9µ        324.8µ        -8.21%

                                    │   old.bench   │              new.bench               │
                                    │     B/op      │     B/op       vs base               │
WalkAllBreadthFirstGnp_10_tenth-8      1.533Ki ± 0%    1.486Ki ± 0%  -3.06% (p=0.000 n=20)
WalkAllBreadthFirstGnp_100_tenth-8     29.58Ki ± 0%    29.61Ki ± 0%  +0.11% (p=0.000 n=20)
WalkAllBreadthFirstGnp_1000_tenth-8   1016.3Ki ± 0%   1016.4Ki ± 0%       ~ (p=0.199 n=20)
WalkAllBreadthFirstGnp_10_half-8       2.689Ki ± 0%    2.721Ki ± 0%  +1.16% (p=0.000 n=20)
WalkAllBreadthFirstGnp_100_half-8      61.44Ki ± 0%    61.47Ki ± 0%  +0.05% (p=0.000 n=20)
WalkAllBreadthFirstGnp_1000_half-8     4.036Mi ± 0%    4.036Mi ± 0%       ~ (p=0.080 n=20)
WalkAllDepthFirstGnp_10_tenth-8        1.533Ki ± 0%    1.486Ki ± 0%  -3.06% (p=0.000 n=20)
WalkAllDepthFirstGnp_100_tenth-8       29.58Ki ± 0%    29.61Ki ± 0%  +0.10% (p=0.000 n=20)
WalkAllDepthFirstGnp_1000_tenth-8      1.090Mi ± 1%    1.084Mi ± 1%       ~ (p=0.081 n=20)
WalkAllDepthFirstGnp_10_half-8         2.689Ki ± 0%    2.721Ki ± 0%  +1.16% (p=0.000 n=20)
WalkAllDepthFirstGnp_100_half-8        61.57Ki ± 0%    61.59Ki ± 0%  +0.04% (p=0.000 n=20)
WalkAllDepthFirstGnp_1000_half-8       6.279Mi ± 0%    6.167Mi ± 0%  -1.79% (p=0.000 n=20)
geomean                                58.77Ki         58.48Ki       -0.49%

                                    │  old.bench  │              new.bench              │
                                    │  allocs/op  │  allocs/op   vs base                │
WalkAllBreadthFirstGnp_10_tenth-8      27.00 ± 0%    17.00 ± 0%  -37.04% (p=0.000 n=20)
WalkAllBreadthFirstGnp_100_tenth-8    1.188k ± 0%   1.088k ± 0%   -8.42% (p=0.000 n=20)
WalkAllBreadthFirstGnp_1000_tenth-8   102.1k ± 0%   101.1k ± 0%   -0.98% (p=0.000 n=20)
WalkAllBreadthFirstGnp_10_half-8       70.00 ± 0%    60.00 ± 0%  -14.29% (p=0.000 n=20)
WalkAllBreadthFirstGnp_100_half-8     5.266k ± 0%   5.166k ± 0%   -1.90% (p=0.000 n=20)
WalkAllBreadthFirstGnp_1000_half-8    500.9k ± 0%   499.9k ± 0%   -0.20% (p=0.000 n=20)
WalkAllDepthFirstGnp_10_tenth-8        27.00 ± 0%    17.00 ± 0%  -37.04% (p=0.000 n=20)
WalkAllDepthFirstGnp_100_tenth-8      1.188k ± 0%   1.088k ± 0%   -8.42% (p=0.000 n=20)
WalkAllDepthFirstGnp_1000_tenth-8     102.1k ± 0%   101.1k ± 0%   -0.98% (p=0.000 n=20)
WalkAllDepthFirstGnp_10_half-8         70.00 ± 0%    60.00 ± 0%  -14.29% (p=0.000 n=20)
WalkAllDepthFirstGnp_100_half-8       5.266k ± 0%   5.166k ± 0%   -1.90% (p=0.000 n=20)
WalkAllDepthFirstGnp_1000_half-8      500.9k ± 0%   499.9k ± 0%   -0.20% (p=0.000 n=20)
geomean                               2.908k        2.572k       -11.54%
2024-05-24 20:49:39 +09:30

207 lines
6.0 KiB
Go

// Copyright ©2018 The Gonum Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
//go:build safe
// +build safe
package iterator
import (
"reflect"
"gonum.org/v1/gonum/graph"
)
// Nodes implements the graph.Nodes interfaces.
// The iteration order of Nodes is randomized.
type Nodes struct {
iter reflect.MapIter
pos, len int
curr graph.Node
value reflect.Value
nodes reflect.Value
}
// NewNodes returns a Nodes initialized with the provided nodes, a
// map of node IDs to graph.Nodes. No check is made that the keys
// match the graph.Node IDs, and the map keys are not used.
//
// Behavior of the Nodes is unspecified if nodes is mutated after
// the call to NewNodes.
func NewNodes(nodes map[int64]graph.Node) *Nodes {
rv := reflect.ValueOf(nodes)
n := &Nodes{nodes: rv, len: len(nodes)}
n.iter.Reset(rv)
n.value = reflect.ValueOf(&n.curr).Elem()
return n
}
// Len returns the remaining number of nodes to be iterated over.
func (n *Nodes) Len() int {
return n.len - n.pos
}
// Next returns whether the next call of Node will return a valid node.
func (n *Nodes) Next() bool {
if n.pos >= n.len {
return false
}
ok := n.iter.Next()
if ok {
n.pos++
n.value.SetIterValue(&n.iter)
}
return ok
}
// Node returns the current node of the iterator. Next must have been
// called prior to a call to Node.
func (n *Nodes) Node() graph.Node {
return n.curr
}
// Reset returns the iterator to its initial state.
func (n *Nodes) Reset() {
n.curr = nil
n.pos = 0
n.iter.Reset(n.nodes)
}
// NodeSlice returns all the remaining nodes in the iterator and advances
// the iterator. The order of nodes within the returned slice is not
// specified.
func (n *Nodes) NodeSlice() []graph.Node {
if n.Len() == 0 {
return nil
}
nodes := make([]graph.Node, 0, n.Len())
for n.iter.Next() {
n.value.SetIterValue(&n.iter)
nodes = append(nodes, n.curr)
}
n.pos = n.len
return nodes
}
// NodesByEdge implements the graph.Nodes interfaces.
// The iteration order of Nodes is randomized.
type NodesByEdge struct {
iter reflect.MapIter
pos, len int
edges reflect.Value
curr graph.Node
nodes map[int64]graph.Node
}
// NewNodesByEdge returns a NodesByEdge initialized with the
// provided nodes, a map of node IDs to graph.Nodes, and the set
// of edges, a map of to-node IDs to graph.Edge, that can be
// traversed to reach the nodes that the NodesByEdge will iterate
// over. No check is made that the keys match the graph.Node IDs,
// and the map keys are not used.
//
// Behavior of the NodesByEdge is unspecified if nodes or edges
// is mutated after the call to NewNodes.
func NewNodesByEdge(nodes map[int64]graph.Node, edges map[int64]graph.Edge) *NodesByEdge {
rv := reflect.ValueOf(edges)
n := &NodesByEdge{nodes: nodes, len: len(edges), edges: rv}
n.iter.Reset(rv)
return n
}
// NewNodesByWeightedEdge returns a NodesByEdge initialized with the
// provided nodes, a map of node IDs to graph.Nodes, and the set
// of edges, a map of to-node IDs to graph.WeightedEdge, that can be
// traversed to reach the nodes that the NodesByEdge will iterate
// over. No check is made that the keys match the graph.Node IDs,
// and the map keys are not used.
//
// Behavior of the NodesByEdge is unspecified if nodes or edges
// is mutated after the call to NewNodes.
func NewNodesByWeightedEdge(nodes map[int64]graph.Node, edges map[int64]graph.WeightedEdge) *NodesByEdge {
rv := reflect.ValueOf(edges)
n := &NodesByEdge{nodes: nodes, len: len(edges), edges: rv}
n.iter.Reset(rv)
return n
}
// NewNodesByLines returns a NodesByEdge initialized with the
// provided nodes, a map of node IDs to graph.Nodes, and the set
// of lines, a map to-node IDs to map of graph.Line, that can be
// traversed to reach the nodes that the NodesByEdge will iterate
// over. No check is made that the keys match the graph.Node IDs,
// and the map keys are not used.
//
// Behavior of the NodesByEdge is unspecified if nodes or lines
// is mutated after the call to NewNodes.
func NewNodesByLines(nodes map[int64]graph.Node, lines map[int64]map[int64]graph.Line) *NodesByEdge {
rv := reflect.ValueOf(lines)
n := &NodesByEdge{nodes: nodes, len: len(lines), edges: rv}
n.iter.Reset(rv)
return n
}
// NewNodesByWeightedLines returns a NodesByEdge initialized with the
// provided nodes, a map of node IDs to graph.Nodes, and the set
// of lines, a map to-node IDs to map of graph.WeightedLine, that can be
// traversed to reach the nodes that the NodesByEdge will iterate
// over. No check is made that the keys match the graph.Node IDs,
// and the map keys are not used.
//
// Behavior of the NodesByEdge is unspecified if nodes or lines
// is mutated after the call to NewNodes.
func NewNodesByWeightedLines(nodes map[int64]graph.Node, lines map[int64]map[int64]graph.WeightedLine) *NodesByEdge {
rv := reflect.ValueOf(lines)
n := &NodesByEdge{nodes: nodes, len: len(lines), edges: rv}
n.iter.Reset(rv)
return n
}
// Len returns the remaining number of nodes to be iterated over.
func (n *NodesByEdge) Len() int {
return n.len - n.pos
}
// Next returns whether the next call of Node will return a valid node.
func (n *NodesByEdge) Next() bool {
if n.pos >= n.len {
return false
}
ok := n.iter.Next()
if ok {
n.pos++
n.curr = n.nodes[n.iter.Key().Int()]
}
return ok
}
// Node returns the current node of the iterator. Next must have been
// called prior to a call to Node.
func (n *NodesByEdge) Node() graph.Node {
return n.curr
}
// Reset returns the iterator to its initial state.
func (n *NodesByEdge) Reset() {
n.curr = nil
n.pos = 0
n.iter.Reset(n.edges)
}
// NodeSlice returns all the remaining nodes in the iterator and advances
// the iterator. The order of nodes within the returned slice is not
// specified.
func (n *NodesByEdge) NodeSlice() []graph.Node {
if n.Len() == 0 {
return nil
}
nodes := make([]graph.Node, 0, n.Len())
for n.iter.Next() {
n.curr = n.nodes[n.iter.Key().Int()]
nodes = append(nodes, n.curr)
}
n.pos = n.len
return nodes
}