import CBenchSupport import Foundation import Gtk import Logging import ProfileRecorderServer @_spi(Portico) import Portico #if canImport(Glibc) import Glibc #endif // Portico benchmark driver. // // Runs one scenario group per invocation so a profile can be attributed to a // single subsystem, or `all` for the full report. // // Profiling: // PROFILE_RECORDER_SERVER_URL_PATTERN=unix:///tmp/portico-{PID}.sock \ // .build/release/PorticoBench all // curl --unix-socket /tmp/portico-.sock \ // -sd '{"numberOfSamples":600,"timeInterval":"5 ms"}' \ // http://localhost/sample | swift demangle --compact > /tmp/portico.perf // // This is top-level code rather than `@main`: the workload must own the main // thread synchronously (GTK is main-thread confined and the scenarios pump the // GLib main context directly), so control never returns to the Swift // concurrency runtime. The sampling server runs on its own NIO threads. let group = CommandLine.arguments.dropFirst().first ?? "all" if ProcessInfo.processInfo.environment["PROFILE_RECORDER_SERVER_URL_PATTERN"] != nil { var logger = Logger(label: "portico-bench") logger.logLevel = .error let serverLogger = logger _Concurrency.Task.detached { try? await ProfileRecorderServer(configuration: .parseFromEnvironment()) .runIgnoringFailures(logger: serverLogger) } // Let the listener bind before the workload saturates the main thread. usleep(400_000) } MainActor.assumeIsolated { // GTK must be initialized before any widget is constructed. Doing it here // keeps GType registration and theme loading out of every measurement. guard Gtk.initCheck() else { FileHandle.standardError.write(Data("gtk init failed; a display is required\n".utf8)) exit(1) } // Force lazy one-time initialization (GType registration for the widget // classes used below, Swift metadata instantiation, malloc arena growth) // before the first measurement window opens. for _ in 0..<200 { let ctx = MountContext() _ = AnyView(Label("warm").hexpand(true)).makeWidget(ctx) ctx.registry.teardown() } let wantMount = group == "all" || group == "mount" let wantState = group == "all" || group == "state" let wantList = group == "all" || group == "list" let wantLoop = group == "all" || group == "loop" if wantMount { Scenarios.mountBareGtkLabel() Scenarios.mountStaticLabel() Scenarios.mountRawLabel() Scenarios.mountModifierChain() Scenarios.mountTree() Scenarios.teardownTree() } if wantState { Scenarios.stateChurnNoDependents() Scenarios.stateChurnOneDependent() Scenarios.stateChurnManyDependents() Scenarios.subscriberChurn() Scenarios.bindingProjection() Scenarios.reactiveLabelUpdate() Scenarios.setAlternatingHalfNoOp() Scenarios.setIfChangedHalfNoOp() Scenarios.twoWayPropertyChurn() Scenarios.twoWayPropertyFanout() Scenarios.trackerAttach() Scenarios.trackerAttachObserving() Scenarios.observationRegistryPressure() Scenarios.observationMutation() } if wantList { Scenarios.forEachMount() Scenarios.forEachAppend() Scenarios.forEachRemoveFirst() Scenarios.forEachReverse() Scenarios.conditionalFlip() } if wantLoop { Scenarios.idleSourceChurn() } Harness.report() } exit(0)