Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: Make it possible to build and run without jemalloc available #127

Merged
merged 1 commit into from
Mar 22, 2023
Merged
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
56 changes: 39 additions & 17 deletions Package.swift
Original file line number Diff line number Diff line change
@@ -1,6 +1,11 @@
// swift-tools-version: 5.7

import PackageDescription
import class Foundation.ProcessInfo

// If the environment variable BENCHMARK_DISABLE_JEMALLOC is set, we'll build the package without Jemalloc support

let disableJemalloc = ProcessInfo.processInfo.environment["BENCHMARK_DISABLE_JEMALLOC"]

let package = Package(
name: "Benchmark",
Expand All @@ -19,7 +24,6 @@ let package = Package(
.package(url: "https://github.com/swift-extras/swift-extras-json", .upToNextMajor(from: "0.6.0")),
// .package(url: "https://github.com/SwiftPackageIndex/SPIManifest", from: "0.12.0"),
.package(url: "https://github.com/ordo-one/TextTable", .upToNextMajor(from: "0.0.1")),
.package(url: "https://github.com/ordo-one/package-jemalloc", .upToNextMajor(from: "1.0.0")),
.package(url: "https://github.com/ordo-one/package-datetime", .upToNextMajor(from: "0.0.0")),
.package(url: "https://github.com/ordo-one/package-histogram", .upToNextMajor(from: "0.0.1")),
.package(url: "https://github.com/ordo-one/Progress.swift", .upToNextMajor(from: "1.0.0")),
Expand Down Expand Up @@ -85,22 +89,6 @@ let package = Package(
path: "Plugins/BenchmarkHelpGenerator"
),

// Benchmark package
.target(
name: "Benchmark",
dependencies: [
.product(name: "Histogram", package: "package-histogram"),
.product(name: "ArgumentParser", package: "swift-argument-parser"),
.product(name: "ExtrasJSON", package: "swift-extras-json"),
.product(name: "SystemPackage", package: "swift-system"),
.product(name: "jemalloc", package: "package-jemalloc"),
.product(name: "DateTime", package: "package-datetime"),
.product(name: "Progress", package: "Progress.swift"),
.byNameItem(name: "CDarwinOperatingSystemStats", condition: .when(platforms: [.macOS])),
.byNameItem(name: "CLinuxOperatingSystemStats", condition: .when(platforms: [.linux])),
]
),

// Getting OS specific information
.target(
name: "CDarwinOperatingSystemStats",
Expand Down Expand Up @@ -154,3 +142,37 @@ let package = Package(
),
]
)

if let disableJemalloc, disableJemalloc != "false", disableJemalloc != "0" {
package.targets += [
.target(
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Factor out common parts of this target?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Will file issue for it and fix next time around.

name: "Benchmark",
dependencies: [
.product(name: "Histogram", package: "package-histogram"),
.product(name: "ArgumentParser", package: "swift-argument-parser"),
.product(name: "ExtrasJSON", package: "swift-extras-json"),
.product(name: "SystemPackage", package: "swift-system"),
.product(name: "DateTime", package: "package-datetime"),
.product(name: "Progress", package: "Progress.swift"),
.byNameItem(name: "CDarwinOperatingSystemStats", condition: .when(platforms: [.macOS])),
.byNameItem(name: "CLinuxOperatingSystemStats", condition: .when(platforms: [.linux])),
]
)]
} else {
package.dependencies += [.package(url: "https://github.com/ordo-one/package-jemalloc", .upToNextMajor(from: "1.0.0"))]
package.targets += [
.target(
name: "Benchmark",
dependencies: [
.product(name: "Histogram", package: "package-histogram"),
.product(name: "ArgumentParser", package: "swift-argument-parser"),
.product(name: "ExtrasJSON", package: "swift-extras-json"),
.product(name: "SystemPackage", package: "swift-system"),
.product(name: "jemalloc", package: "package-jemalloc"),
.product(name: "DateTime", package: "package-datetime"),
.product(name: "Progress", package: "Progress.swift"),
.byNameItem(name: "CDarwinOperatingSystemStats", condition: .when(platforms: [.macOS])),
.byNameItem(name: "CLinuxOperatingSystemStats", condition: .when(platforms: [.linux])),
]
)]
}
14 changes: 11 additions & 3 deletions Sources/Benchmark/Documentation.docc/GettingStarted.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,11 +16,19 @@ After having done those, running your benchmarks are as simple as running `swift

Benchmark requires Swift 5.7 support as it uses Regex and Duration types introduced with the `macOS 13` runtime, most versions of Linux will work as long as Swift 5.7+ is used.

Benchmark also depends and needs the [jemalloc](https://jemalloc.net) memory allocation library, which is used by the Benchmark infrastructure to capture memory allocation statistics, as `jemalloc` provides a rich programmatic API for extracting memory allocation statistics at runtime.
Benchmark also by default depends on and uses the [jemalloc](https://jemalloc.net) memory allocation library, which is used by the Benchmark infrastructure to capture memory allocation statistics.

The Benchmark package requires you to install jemalloc on any machine used for benchmarking.
For platforms where `jemalloc` isn't available it's possible to build the Benchmark package without a `jemalloc` dependency by setting the environment variable BENCHMARK_DISABLE_JEMALLOC to any value except `false` or `0`.

If you want to avoid adding the `jemalloc` dependency to your project, the recommended approach is to embed a separate Swift project in a subdirectory that uses your project, then the dependency on `jemalloc` is contained to that subproject only.
E.g. to run the benchmark on the command line without memory allocation stats could look like:

```bash
BENCHMARK_DISABLE_JEMALLOC=true swift package benchmark
```

The Benchmark package requires you to install jemalloc on any machine used for benchmarking if you want malloc statistics.

If you want to avoid adding the `jemalloc` dependency to your main project while still getting malloc statistics when benchmarking, the recommended approach is to embed a separate Swift project in a subdirectory that uses your project, then the dependency on `jemalloc` is contained to that subproject only.

#### Installing `jemalloc` on macOS

Expand Down
16 changes: 16 additions & 0 deletions Sources/Benchmark/MallocStats/MallocStatsProducer+jemalloc.swift
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@
//

import ExtrasJSON

#if canImport(jemalloc)
import jemalloc

// We currently register a number of MIB:s that aren't in use that
Expand Down Expand Up @@ -165,3 +167,17 @@ class MallocStatsProducer {
return carrier.data
}
}

#else

// stub if no jemalloc available
class MallocStatsProducer {
func makeMallocStats() -> MallocStats {
return MallocStats(mallocCountTotal: 0,
mallocCountSmall: 0,
mallocCountLarge: 0,
allocatedResidentMemory: 0)
}
}

#endif
3 changes: 2 additions & 1 deletion Tests/BenchmarkTests/OperatingSystemAndMallocTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ final class OperatingSystemAndMallocTests: XCTestCase {
blackHole(operatingSystemStatsProducer.metricSupported(.writeBytesPhysical))
blackHole(operatingSystemStatsProducer.metricSupported(.throughput))
}

#if canImport(jemalloc)
func testMallocProducerLeaks() throws {
let mallocStatsProducer = MallocStatsProducer()
let startMallocStats = mallocStatsProducer.makeMallocStats()
Expand All @@ -68,4 +68,5 @@ final class OperatingSystemAndMallocTests: XCTestCase {
XCTAssertGreaterThanOrEqual(stopMallocStats.allocatedResidentMemory - startMallocStats.allocatedResidentMemory,
100 * 1_024)
}
#endif
}