Files
Timofey Solomko 51734eb8d3 [swcomp] Remove empty warmupIteration function from comp-ratio benchmarks
These functions weren't called anyway since 5e8f568f64 commit where Benchmark protocol requirement for this function acquired Double return type. In any event, skipping of warmup iteration is better controlled by the corresponding option of "benchmark run" command.
2026-02-23 18:08:26 +08:00

644 lines
19 KiB
Swift

// Copyright (c) 2026 Timofey Solomko
// Licensed under MIT License
//
// See LICENSE for license information
#if os(Linux)
import CoreFoundation
#endif
import Foundation
import SWCompression
import SwiftCLI
enum Benchmarks: String, CaseIterable, ConvertibleFromString {
case unGzip = "un-gzip"
case unBz2 = "un-bz2"
case unLz4 = "un-lz4"
case unXz = "un-xz"
case compDeflate = "comp-deflate"
case compRatioDeflate = "comp-ratio-deflate"
case compBz2 = "comp-bz2"
case compRatioBz2 = "comp-ratio-bz2"
case compLz4 = "comp-lz4"
case compRatioLz4 = "comp-ratio-lz4"
case compLz4Bd = "comp-lz4-bd"
case compRatioLz4Bd = "comp-ratio-lz4-bd"
case info7z = "info-7z"
case infoTar = "info-tar"
case infoZip = "info-zip"
case createTar = "create-tar"
case readerTar = "reader-tar"
case writerTar = "writer-tar"
var titleName: String {
switch self {
case .unGzip:
return "GZip Unarchive"
case .unBz2:
return "BZip2 Decompress"
case .unLz4:
return "LZ4 Decompress"
case .unXz:
return "XZ Unarchive"
case .compDeflate:
return "Deflate Compress"
case .compRatioDeflate:
return "Deflate Compression Ratio"
case .compBz2:
return "BZip2 Compress"
case .compRatioBz2:
return "BZip2 Compression Ratio"
case .compLz4:
return "LZ4 Compress"
case .compRatioLz4:
return "LZ4 Compression Ratio"
case .compLz4Bd:
return "LZ4 Compress (dependent blocks)"
case .compRatioLz4Bd:
return "LZ4 Compression Ratio (dependent blocks)"
case .info7z:
return "7-Zip Info"
case .infoTar:
return "TAR Info"
case .infoZip:
return "ZIP Info"
case .createTar:
return "TAR Create"
case .readerTar:
return "TAR Reader"
case .writerTar:
return "TAR Writer"
}
}
func initialized(_ input: String) -> Benchmark {
switch self {
case .unGzip:
return UnGzip(input)
case .unBz2:
return UnBz2(input)
case .unLz4:
return UnLz4(input)
case .unXz:
return UnXz(input)
case .compDeflate:
return CompDeflate(input)
case .compRatioDeflate:
return CompRatioDeflate(input)
case .compBz2:
return CompBz2(input)
case .compRatioBz2:
return CompRatioBz2(input)
case .compLz4:
return CompLz4(input)
case .compRatioLz4:
return CompRatioLz4(input)
case .compLz4Bd:
return CompLz4Bd(input)
case .compRatioLz4Bd:
return CompRatioLz4Bd(input)
case .info7z:
return Info7z(input)
case .infoTar:
return InfoTar(input)
case .infoZip:
return InfoZip(input)
case .createTar:
return CreateTar(input)
case .readerTar:
return ReaderTar(input)
case .writerTar:
return WriterTar(input)
}
}
static var allBenchmarks: String {
return Self.allCases.map { $0.rawValue }.joined(separator: ", ")
}
}
struct UnGzip: Benchmark {
private let data: Data
private let size: Double
init(_ input: String) {
do {
let inputURL = URL(fileURLWithPath: input)
self.data = try Data(contentsOf: inputURL, options: .mappedIfSafe)
self.size = Double(self.data.count)
} catch let error {
swcompExit(.benchmarkCannotSetup(Self.self, input, error))
}
}
func measure() -> Double {
do {
let startTime = DispatchTime.now().uptimeNanoseconds
_ = try GzipArchive.unarchive(archive: self.data)
let timeElapsed = Double(DispatchTime.now().uptimeNanoseconds - startTime) / 1_000_000_000
return self.size / timeElapsed
} catch let error {
swcompExit(.benchmarkCannotMeasure(Self.self, error))
}
}
}
struct UnBz2: Benchmark {
private let data: Data
private let size: Double
init(_ input: String) {
do {
let inputURL = URL(fileURLWithPath: input)
self.data = try Data(contentsOf: inputURL, options: .mappedIfSafe)
self.size = Double(self.data.count)
} catch let error {
swcompExit(.benchmarkCannotSetup(Self.self, input, error))
}
}
func measure() -> Double {
do {
let startTime = DispatchTime.now().uptimeNanoseconds
_ = try BZip2.decompress(data: self.data)
let timeElapsed = Double(DispatchTime.now().uptimeNanoseconds - startTime) / 1_000_000_000
return self.size / timeElapsed
} catch let error {
swcompExit(.benchmarkCannotMeasure(Self.self, error))
}
}
}
struct UnLz4: Benchmark {
private let data: Data
private let size: Double
init(_ input: String) {
do {
let inputURL = URL(fileURLWithPath: input)
self.data = try Data(contentsOf: inputURL, options: .mappedIfSafe)
self.size = Double(self.data.count)
} catch let error {
swcompExit(.benchmarkCannotSetup(Self.self, input, error))
}
}
func measure() -> Double {
do {
let startTime = DispatchTime.now().uptimeNanoseconds
_ = try LZ4.decompress(data: self.data)
let timeElapsed = Double(DispatchTime.now().uptimeNanoseconds - startTime) / 1_000_000_000
return self.size / timeElapsed
} catch let error {
swcompExit(.benchmarkCannotMeasure(Self.self, error))
}
}
}
struct UnXz: Benchmark {
private let data: Data
private let size: Double
init(_ input: String) {
do {
let inputURL = URL(fileURLWithPath: input)
self.data = try Data(contentsOf: inputURL, options: .mappedIfSafe)
self.size = Double(self.data.count)
} catch let error {
swcompExit(.benchmarkCannotSetup(Self.self, input, error))
}
}
func measure() -> Double {
do {
let startTime = DispatchTime.now().uptimeNanoseconds
_ = try XZArchive.unarchive(archive: self.data)
let timeElapsed = Double(DispatchTime.now().uptimeNanoseconds - startTime) / 1_000_000_000
return self.size / timeElapsed
} catch let error {
swcompExit(.benchmarkCannotMeasure(Self.self, error))
}
}
}
struct CompDeflate: Benchmark {
private let data: Data
private let size: Double
init(_ input: String) {
do {
let inputURL = URL(fileURLWithPath: input)
self.data = try Data(contentsOf: inputURL, options: .mappedIfSafe)
self.size = Double(self.data.count)
} catch let error {
swcompExit(.benchmarkCannotSetup(Self.self, input, error))
}
}
func measure() -> Double {
let startTime = DispatchTime.now().uptimeNanoseconds
_ = Deflate.compress(data: self.data)
let timeElapsed = Double(DispatchTime.now().uptimeNanoseconds - startTime) / 1_000_000_000
return self.size / timeElapsed
}
}
struct CompRatioDeflate: Benchmark {
let defaultIterationCount: Int = 1
private let data: Data
private let size: Double
init(_ input: String) {
do {
let inputURL = URL(fileURLWithPath: input)
self.data = try Data(contentsOf: inputURL, options: .mappedIfSafe)
self.size = Double(self.data.count)
} catch let error {
swcompExit(.benchmarkCannotSetup(Self.self, input, error))
}
}
func measure() -> Double {
let outputData = Deflate.compress(data: self.data)
guard outputData.count > 0
else { swcompExit(.benchmarkCannotMeasureBadOutSize(Self.self)) }
return Double(self.size) / Double(outputData.count)
}
func format(_ value: Double) -> String {
return String(format: "%.3f", value)
}
}
struct CompBz2: Benchmark {
private let data: Data
private let size: Double
init(_ input: String) {
do {
let inputURL = URL(fileURLWithPath: input)
self.data = try Data(contentsOf: inputURL, options: .mappedIfSafe)
self.size = Double(self.data.count)
} catch let error {
swcompExit(.benchmarkCannotSetup(Self.self, input, error))
}
}
func measure() -> Double {
let startTime = DispatchTime.now().uptimeNanoseconds
_ = BZip2.compress(data: self.data)
let timeElapsed = Double(DispatchTime.now().uptimeNanoseconds - startTime) / 1_000_000_000
return self.size / timeElapsed
}
}
struct CompRatioBz2: Benchmark {
let defaultIterationCount: Int = 1
private let data: Data
private let size: Double
init(_ input: String) {
do {
let inputURL = URL(fileURLWithPath: input)
self.data = try Data(contentsOf: inputURL, options: .mappedIfSafe)
self.size = Double(self.data.count)
} catch let error {
swcompExit(.benchmarkCannotSetup(Self.self, input, error))
}
}
func measure() -> Double {
let outputData = BZip2.compress(data: self.data)
guard outputData.count > 0
else { swcompExit(.benchmarkCannotMeasureBadOutSize(Self.self)) }
return Double(self.size) / Double(outputData.count)
}
func format(_ value: Double) -> String {
return String(format: "%.3f", value)
}
}
struct CompLz4: Benchmark {
private let data: Data
private let size: Double
init(_ input: String) {
do {
let inputURL = URL(fileURLWithPath: input)
self.data = try Data(contentsOf: inputURL, options: .mappedIfSafe)
self.size = Double(self.data.count)
} catch let error {
swcompExit(.benchmarkCannotSetup(Self.self, input, error))
}
}
func measure() -> Double {
let startTime = DispatchTime.now().uptimeNanoseconds
_ = LZ4.compress(data: self.data)
let timeElapsed = Double(DispatchTime.now().uptimeNanoseconds - startTime) / 1_000_000_000
return self.size / timeElapsed
}
}
struct CompRatioLz4: Benchmark {
let defaultIterationCount: Int = 1
private let data: Data
private let size: Double
init(_ input: String) {
do {
let inputURL = URL(fileURLWithPath: input)
self.data = try Data(contentsOf: inputURL, options: .mappedIfSafe)
self.size = Double(self.data.count)
} catch let error {
swcompExit(.benchmarkCannotSetup(Self.self, input, error))
}
}
func measure() -> Double {
let outputData = LZ4.compress(data: self.data)
guard outputData.count > 0
else { swcompExit(.benchmarkCannotMeasureBadOutSize(Self.self)) }
return Double(self.size) / Double(outputData.count)
}
func format(_ value: Double) -> String {
return String(format: "%.3f", value)
}
}
struct CompLz4Bd: Benchmark {
private let data: Data
private let size: Double
init(_ input: String) {
do {
let inputURL = URL(fileURLWithPath: input)
self.data = try Data(contentsOf: inputURL, options: .mappedIfSafe)
self.size = Double(self.data.count)
} catch let error {
swcompExit(.benchmarkCannotSetup(Self.self, input, error))
}
}
func measure() -> Double {
let startTime = DispatchTime.now().uptimeNanoseconds
_ = LZ4.compress(data: self.data, independentBlocks: false, blockChecksums: false, contentChecksum: true,
contentSize: false, blockSize: 4 * 1024 * 1024, dictionary: nil, dictionaryID: nil)
let timeElapsed = Double(DispatchTime.now().uptimeNanoseconds - startTime) / 1_000_000_000
return self.size / timeElapsed
}
}
struct CompRatioLz4Bd: Benchmark {
let defaultIterationCount: Int = 1
private let data: Data
private let size: Double
init(_ input: String) {
do {
let inputURL = URL(fileURLWithPath: input)
self.data = try Data(contentsOf: inputURL, options: .mappedIfSafe)
self.size = Double(self.data.count)
} catch let error {
swcompExit(.benchmarkCannotSetup(Self.self, input, error))
}
}
func measure() -> Double {
let outputData = LZ4.compress(data: self.data, independentBlocks: false, blockChecksums: false,
contentChecksum: true, contentSize: false, blockSize: 4 * 1024 * 1024, dictionary: nil, dictionaryID: nil)
guard outputData.count > 0
else { swcompExit(.benchmarkCannotMeasureBadOutSize(Self.self)) }
return Double(self.size) / Double(outputData.count)
}
func format(_ value: Double) -> String {
return String(format: "%.3f", value)
}
}
struct Info7z: Benchmark {
private let data: Data
private let size: Double
init(_ input: String) {
do {
let inputURL = URL(fileURLWithPath: input)
self.data = try Data(contentsOf: inputURL, options: .mappedIfSafe)
self.size = Double(self.data.count)
} catch let error {
swcompExit(.benchmarkCannotSetup(Self.self, input, error))
}
}
func measure() -> Double {
do {
let startTime = DispatchTime.now().uptimeNanoseconds
_ = try SevenZipContainer.info(container: self.data)
let timeElapsed = Double(DispatchTime.now().uptimeNanoseconds - startTime) / 1_000_000_000
return self.size / timeElapsed
} catch let error {
swcompExit(.benchmarkCannotMeasure(Self.self, error))
}
}
}
struct InfoTar: Benchmark {
private let data: Data
private let size: Double
init(_ input: String) {
do {
let inputURL = URL(fileURLWithPath: input)
self.data = try Data(contentsOf: inputURL, options: .mappedIfSafe)
self.size = Double(self.data.count)
} catch let error {
swcompExit(.benchmarkCannotSetup(Self.self, input, error))
}
}
func measure() -> Double {
do {
let startTime = DispatchTime.now().uptimeNanoseconds
_ = try TarContainer.info(container: self.data)
let timeElapsed = Double(DispatchTime.now().uptimeNanoseconds - startTime) / 1_000_000_000
return self.size / timeElapsed
} catch let error {
swcompExit(.benchmarkCannotMeasure(Self.self, error))
}
}
}
struct InfoZip: Benchmark {
private let data: Data
private let size: Double
init(_ input: String) {
do {
let inputURL = URL(fileURLWithPath: input)
self.data = try Data(contentsOf: inputURL, options: .mappedIfSafe)
self.size = Double(self.data.count)
} catch let error {
swcompExit(.benchmarkCannotSetup(Self.self, input, error))
}
}
func measure() -> Double {
do {
let startTime = DispatchTime.now().uptimeNanoseconds
_ = try ZipContainer.info(container: self.data)
let timeElapsed = Double(DispatchTime.now().uptimeNanoseconds - startTime) / 1_000_000_000
return self.size / timeElapsed
} catch let error {
swcompExit(.benchmarkCannotMeasure(Self.self, error))
}
}
}
struct CreateTar: Benchmark {
private let entries: [TarEntry]
private let size: Double
init(_ input: String) {
do {
self.entries = try TarEntry.createEntries(input, false)
self.size = try Double(URL(fileURLWithPath: input).directorySize())
} catch let error {
swcompExit(.benchmarkCannotSetup(Self.self, input, error))
}
}
func measure() -> Double {
let startTime = DispatchTime.now().uptimeNanoseconds
_ = TarContainer.create(from: self.entries)
let timeElapsed = Double(DispatchTime.now().uptimeNanoseconds - startTime) / 1_000_000_000
return self.size / timeElapsed
}
}
struct ReaderTar: Benchmark {
private let url: URL
private let size: Double
init(_ input: String) {
do {
self.url = URL(fileURLWithPath: input)
let resourceValues = try self.url.resourceValues(forKeys: [.fileSizeKey])
if let fileSize = resourceValues.fileSize {
self.size = Double(fileSize)
} else {
swcompExit(.benchmarkReaderTarNoInputSize(input))
}
} catch let error {
swcompExit(.benchmarkCannotSetup(Self.self, input, error))
}
}
func measure() -> Double {
do {
let handle = try FileHandle(forReadingFrom: self.url)
let startTime = DispatchTime.now().uptimeNanoseconds
var reader = TarReader(fileHandle: handle)
var isFinished = false
var infos = [TarEntryInfo]()
while !isFinished {
isFinished = try reader.process { (entry: TarEntry?) -> Bool in
guard entry != nil
else { return true }
infos.append(entry!.info)
return false
}
}
let timeElapsed = Double(DispatchTime.now().uptimeNanoseconds - startTime) / 1_000_000_000
try handle.close()
return self.size / timeElapsed
} catch let error {
swcompExit(.benchmarkCannotMeasure(Self.self, error))
}
}
}
struct WriterTar: Benchmark {
private let entries: [TarEntry]
private let size: Double
init(_ input: String) {
do {
self.entries = try TarEntry.createEntries(input, false)
self.size = try Double(URL(fileURLWithPath: input).directorySize())
} catch let error {
swcompExit(.benchmarkCannotSetup(Self.self, input, error))
}
}
func measure() -> Double {
do {
let url = URL(fileURLWithPath: NSTemporaryDirectory()).appendingPathComponent(UUID().uuidString, isDirectory: false)
try "".write(to: url, atomically: true, encoding: .utf8)
let handle = try FileHandle(forWritingTo: url)
let startTime = DispatchTime.now().uptimeNanoseconds
var writer = TarWriter(fileHandle: handle)
for entry in self.entries {
try writer.append(entry)
}
try writer.finalize()
let timeElapsed = Double(DispatchTime.now().uptimeNanoseconds - startTime) / 1_000_000_000
try handle.close()
try FileManager.default.removeItem(at: url)
return self.size / timeElapsed
} catch let error {
swcompExit(.benchmarkCannotMeasure(Self.self, error))
}
}
}
fileprivate extension URL {
func directorySize() throws -> Int {
let urls = FileManager.default.enumerator(at: self, includingPropertiesForKeys: nil)?.allObjects as! [URL]
return try urls.lazy.reduce(0) {
(try $1.resourceValues(forKeys: [.totalFileAllocatedSizeKey]).totalFileAllocatedSize ?? 0) + $0
}
}
}