Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
36 changes: 31 additions & 5 deletions R/iterators.R
Original file line number Diff line number Diff line change
Expand Up @@ -313,8 +313,21 @@ unsafe_create_vs <- function(graph, idx, verts = NULL) {
if (is.null(verts)) {
verts <- V(graph)
}
res <- simple_vs_index(verts, idx, na_ok = TRUE)
add_vses_graph_ref(res, graph)
# `idx` are vertex IDs straight from C, and `verts` is the full `V(graph)`,
# so `verts[idx]` would just be `idx` again -- skip that copy and use the
# IDs directly as the payload. Names are subset from `verts`, and the graph
# reference is shared from `verts`. All attributes are set in one
# `attributes<-` call to avoid the per-object shallow copies that dominate
# when many sequences are built (e.g. `max_cliques()`).
vertex_names <- attr(verts, "names")
res <- as.integer(idx)
attributes(res) <- list(
names = if (is.null(vertex_names)) NULL else vertex_names[idx],
class = "igraph.vs",
env = attr(verts, "env"),
graph = attr(verts, "graph")
)
res
}

# Internal function to quickly convert integer vectors to igraph.es
Expand All @@ -325,8 +338,9 @@ unsafe_create_es <- function(graph, idx, es = NULL) {
if (is.null(es)) {
es <- E(graph)
}
res <- simple_es_index(es, idx, na_ok = TRUE)
add_vses_graph_ref(res, graph)
# `simple_es_index()` already carries the graph reference over from `es`,
# so the weak reference built once by `E(graph)` is shared across calls.
simple_es_index(es, idx, na_ok = TRUE)
}


Expand Down Expand Up @@ -487,7 +501,19 @@ simple_vs_index <- function(x, i, na_ok = FALSE) {
if (!na_ok && anyNA(res)) {
cli::cli_abort("Unknown vertex selected.")
}
class(res) <- "igraph.vs"
# Set every attribute in a single `attributes<-` call rather than one
# `attr<-`/`class<-` at a time: each incremental assignment shallow-copies
# the vector, and that copying dominates when many sequences are built
# (e.g. `max_cliques()`). `names` is carried over from the subset above;
# env/graph are carried from `x`, mirroring `simple_es_index()`, so
# sequences derived from one `V(graph)` share its weak reference instead of
# each minting a fresh one.
attributes(res) <- list(
names = attr(res, "names"),
class = "igraph.vs",
env = attr(x, "env"),
graph = attr(x, "graph")
)
res
}

Expand Down
77 changes: 77 additions & 0 deletions touchstone/script.R
Original file line number Diff line number Diff line change
Expand Up @@ -254,5 +254,82 @@ benchmark_run(
n = 20
)

# ---------------------------------------------------------------------------
# Group #5 - vertex/edge sequence construction on named graphs
# Functions that return (many) vertex/edge sequences pay for building the
# `names`/`vnames` attribute and attaching a graph reference to every object.
# These benchmarks exercise that construction path on *named* graphs, where
# the cost is highest. `max_cliques()` is the canonical case: it returns tens
# of thousands of vertex sequences, one per clique.
# ---------------------------------------------------------------------------
benchmark_run(
expr_before_benchmark = {
library(igraph)
set.seed(42)
g <- sample_gnp(200L, 0.16, directed = FALSE)
V(g)$name <- paste0("v", seq_len(gorder(g)))
for (i in 1:2) {
max_cliques(g)
}
gc(full = TRUE)
},
max_cliques_named = for (i in 1:4) {
max_cliques(g)
},
n = 20
)

benchmark_run(
expr_before_benchmark = {
library(igraph)
set.seed(42)
g <- sample_gnm(1000L, 5000L)
V(g)$name <- paste0("v", seq_len(1000L))
es <- E(g)
for (i in 1:5) {
head_of(g, es)
}
gc(full = TRUE)
},
head_of_named = for (i in 1:320) {
head_of(g, es)
},
n = 20
)

benchmark_run(
expr_before_benchmark = {
library(igraph)
set.seed(42)
g <- sample_gnm(20000L, 50000L)
V(g)$name <- paste0("v", seq_len(20000L))
for (i in 1:5) {
V(g)
}
gc(full = TRUE)
},
V_named = for (i in 1:2700) {
V(g)
},
n = 20
)

benchmark_run(
expr_before_benchmark = {
library(igraph)
set.seed(42)
g <- sample_gnm(20000L, 50000L)
V(g)$name <- paste0("v", seq_len(20000L))
for (i in 1:2) {
E(g)
}
gc(full = TRUE)
},
E_named = for (i in 1:15) {
E(g)
},
n = 20
)

# Create the artifacts consumed by the GitHub Action.
benchmark_analyze()
Loading