perf(parser): add collection capacity hints to known-size loops (#1791)

- For result sets with known sizes, it's more efficient
  to supply the size as a capacity hint.
This commit is contained in:
Hugo, CY LAU
2026-06-25 19:42:37 -07:00
committed by GitHub
parent af71f87408
commit 0c95007763
3 changed files with 83 additions and 5 deletions
@@ -24,6 +24,14 @@ import ContainerizationOS
import Foundation
import SystemPackage
// MARK: - Collection capacity hints
// Methods in this file build arrays and dictionaries in loops where the final
// size is known from the input parameter count. reserveCapacity() and
// Dictionary(minimumCapacity:) avoid O(log n) reallocation copies as the
// collection grows incrementally. While this is a micro-optimization for each
// individual call, these methods execute on every `container run/create` and
// the savings compound at scale.
/// A parsed volume specification from user input
public struct ParsedVolume {
public let name: String
@@ -124,7 +132,7 @@ public struct Parser {
}
combined.append(contentsOf: Parser.env(envList: envs))
let deduped = combined.reduce(into: [String: String]()) { map, entry in
let deduped = combined.reduce(into: [String: String](minimumCapacity: combined.count)) { map, entry in
let key = String(entry.split(separator: "=", maxSplits: 1).first ?? Substring(entry))
map[key] = entry
}
@@ -232,7 +240,7 @@ public struct Parser {
}
public static func labels(_ rawLabels: [String]) throws -> [String: String] {
var result: [String: String] = [:]
var result: [String: String] = Dictionary(minimumCapacity: rawLabels.count)
for label in rawLabels {
if label.isEmpty {
throw ContainerizationError(.invalidArgument, message: "label cannot be an empty string")
@@ -330,8 +338,9 @@ public struct Parser {
public static let defaultDirectives = ["type": "virtiofs"]
public static func tmpfsMounts(_ mounts: [String]) throws -> [Filesystem] {
var result: [Filesystem] = []
let mounts = mounts.dedupe()
var result: [Filesystem] = []
result.reserveCapacity(mounts.count)
for tmpfs in mounts {
let fs = Filesystem.tmpfs(destination: tmpfs, options: [])
try validateMount(.filesystem(fs))
@@ -341,8 +350,9 @@ public struct Parser {
}
public static func mounts(_ rawMounts: [String], relativeTo basePath: URL? = nil) throws -> [VolumeOrFilesystem] {
var mounts: [VolumeOrFilesystem] = []
let rawMounts = rawMounts.dedupe()
var mounts: [VolumeOrFilesystem] = []
mounts.reserveCapacity(rawMounts.count)
for mount in rawMounts {
let m = try Parser.mount(mount, relativeTo: basePath)
try validateMount(m)
@@ -484,6 +494,7 @@ public struct Parser {
public static func volumes(_ rawVolumes: [String], relativeTo basePath: URL? = nil) throws -> [VolumeOrFilesystem] {
var mounts: [VolumeOrFilesystem] = []
mounts.reserveCapacity(rawVolumes.count)
for volume in rawVolumes {
let m = try Parser.volume(volume, relativeTo: basePath)
try Parser.validateMount(m)
@@ -593,6 +604,7 @@ public struct Parser {
/// - Throws: ContainerizationError if parsing fails
public static func publishPorts(_ rawPublishPorts: [String]) throws -> [PublishPort] {
var publishPorts: [PublishPort] = []
publishPorts.reserveCapacity(rawPublishPorts.count)
// Process each raw port string
for socket in rawPublishPorts {
@@ -726,6 +738,7 @@ public struct Parser {
/// - Throws: ContainerizationError if parsing fails or a path is invalid
public static func publishSockets(_ rawPublishSockets: [String]) throws -> [PublishSocket] {
var sockets: [PublishSocket] = []
sockets.reserveCapacity(rawPublishSockets.count)
// Process each raw socket string
for socket in rawPublishSockets {
@@ -919,6 +932,7 @@ public struct Parser {
/// - nofile=1024:unlimited (soft=1024, hard=UINT64_MAX)
public static func rlimits(_ rawUlimits: [String]) throws -> [ProcessConfiguration.Rlimit] {
var rlimits: [ProcessConfiguration.Rlimit] = []
rlimits.reserveCapacity(rawUlimits.count)
var seenTypes: Set<String> = []
for ulimit in rawUlimits {
@@ -1010,6 +1024,7 @@ public struct Parser {
/// Returns normalized uppercase CAP_* strings.
public static func capabilities(capAdd: [String], capDrop: [String]) throws -> (capAdd: [String], capDrop: [String]) {
var normalizedAdd: [String] = []
normalizedAdd.reserveCapacity(capAdd.count)
for cap in capAdd {
let upper = cap.uppercased()
if upper == "ALL" {
@@ -1024,6 +1039,7 @@ public struct Parser {
}
var normalizedDrop: [String] = []
normalizedDrop.reserveCapacity(capDrop.count)
for cap in capDrop {
let upper = cap.uppercased()
if upper == "ALL" {
@@ -24,6 +24,11 @@ import Foundation
import Logging
import TerminalProgress
// MARK: - Collection capacity hints
// Dictionary(minimumCapacity:) and reserveCapacity() are used in this file to
// pre-allocate storage when the final collection size is known from the input.
// This avoids incremental reallocation overhead in hot-path parser methods.
public struct Utility {
static let publishedPortCountLimit = 64
@@ -349,7 +354,7 @@ public struct Utility {
/// - Parameter pairs: Array of strings in "key=value" format
/// - Returns: Dictionary mapping keys to values
public static func parseKeyValuePairs(_ pairs: [String]) -> [String: String] {
var result: [String: String] = [:]
var result: [String: String] = Dictionary(minimumCapacity: pairs.count)
for pair in pairs {
let components = pair.split(separator: "=", maxSplits: 1)
if components.count == 2 {
@@ -1336,4 +1336,61 @@ struct ParserTest {
func testManagementFlagsAcceptsNoDNSAlone() throws {
_ = try Flags.Management.parse(["--no-dns"])
}
// MARK: - Collection capacity hints
@Test("labels with large input preserves all entries")
func testLabelsLargeInput() throws {
let labels = (0..<100).map { "key\($0)=value\($0)" }
let result = try Parser.labels(labels)
#expect(result.count == 100)
#expect(result["key42"] == "value42")
#expect(result["key99"] == "value99")
}
@Test("resolve with large input preserves all entries")
func testParseKeyValuePairsLargeInput() {
let pairs = (0..<100).map { "key\($0)=value\($0)" }
let result = Utility.parseKeyValuePairs(pairs)
#expect(result.count == 100)
#expect(result["key0"] == "value0")
#expect(result["key99"] == "value99")
}
@Test("tmpfsMounts with large input")
func testTmpfsMountsLargeInput() throws {
let mounts = (0..<20).map { "/mnt/tmpfs\($0)" }
let result = try Parser.tmpfsMounts(mounts)
#expect(result.count == 20)
}
@Test("volumes with large input")
func testVolumesLargeInput() throws {
let volumes = (0..<20).map { "vol\($0):/mnt/vol\($0)" }
let result = try Parser.volumes(volumes)
#expect(result.count == 20)
}
@Test("capabilities with large input")
func testCapabilitiesLargeInput() throws {
let result = try Parser.capabilities(capAdd: ["ALL", "SYS_ADMIN", "NET_RAW", "CHOWN"], capDrop: ["SETUID", "KILL"])
#expect(result.capAdd.count == 4)
#expect(result.capDrop.count == 2)
#expect(result.capAdd.first == "ALL")
}
@Test("rlimits with large input")
func testRlimitsLargeInput() throws {
let result = try Parser.rlimits(["nofile=1024:2048", "nproc=100:200", "memlock=65536:65536"])
#expect(result.count == 3)
#expect(result[0].limit == "RLIMIT_NOFILE")
}
@Test("allEnv with large env lists")
func testAllEnvLargeInput() throws {
let imageEnvs = (0..<50).map { "IMAGE_VAR\($0)=value\($0)" }
let envs = (0..<50).map { "USER_VAR\($0)=value\($0)" }
let result = try Parser.allEnv(imageEnvs: imageEnvs, envFiles: [], envs: envs)
#expect(result.count == 100)
}
}