Spaces:
Sleeping
Sleeping
File size: 5,662 Bytes
6ee917b | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 | import Foundation
import Vapor
import OmFileFormat
fileprivate extension String {
func pad(_ n: Int) -> String {
return padding(toLength: n, withPad: " ", startingAt: 0)
}
static func dash(_ n: Int) -> String {
return "".padding(toLength: n, withPad: "-", startingAt: 0)
}
}
final class BenchmarkCommand: Command {
var help: String { "Benchmark Open-Meteo core functions like data manipulation and compression" }
struct Signature: CommandSignature {
@Option(name: "time", short: "t", help: "Time per test in seconds")
var time: Int?
}
/// `swift run -c release openmeteo-api benchmark`
func run(using context: CommandContext, signature: Signature) throws {
let run = BenchmarkRun(timePerTest: signature.time ?? 5)
print("Open-Meteo Benchmark")
print("Apple M1 is used as baseline. Positive values = slower than M1.")
print("Time per test \(run.timePerTest) seconds (See --help)")
print("| \("Test".pad(80)) | \("Mean".pad(8)) | \("Min".pad(8)) | \("Max".pad(8)) | \("Runs".pad(8)) | \("Diff to Apple M1".pad(20)) |")
print("|\(String.dash(80+2))|\(String.dash(8+2))|\(String.dash(8+2))|\(String.dash(8+2))|\(String.dash(8+2))|\(String.dash(20+2))|")
run.measure("Solar Position Calculation for 50 years, hourly", 625) {
let _ = SolarPositionAlgorithm.sunPosition(timerange: TimerangeDt(start: Timestamp(1950,1,1), to: Timestamp(2000,1,1), dtSeconds: 3600))
}
/*let sizeMb = 128
let data = run.measure("Generating dummy temperature timeseries (\(sizeMb) MB)", 272) {
return (0..<1024*1024/4*sizeMb).map({
let x = Float($0)
return sin(x * .pi / 24) * 10 + sin(x * .pi / 24 / 365.25) * 15 + sin(x * .pi / 7)
})
}
try run.measure("Compression in memory, large chunks", 191) {
try OmFileWriter(dim0: data.count / 1024, dim1: 1024, chunk0: 1024, chunk1: 1024).writeInMemory(compressionType: .pfor_delta2d_int16, scalefactor: 20, all: data)
}*/
/*let compressed = try run.measure("Compression in memory, small chunks", 199) {
try OmFileWriter(dim0: data.count / 1024, dim1: 1024, chunk0: 8, chunk1: 128).writeInMemory(compressionType: .pfor_delta2d_int16, scalefactor: 20, all: data)
}
let sizeMbCompressed = (compressed.count/1024/1024)
let compressedData = DataAsClass(data: compressed)
_ = try run.measure("Decompress from memory, small chunks (\(sizeMbCompressed) MB)", 44) {
try OmFileReader(fn: compressedData).readAll()
}
let file = "\(OpenMeteo.dataDirectory)test.om"
try FileManager.default.createDirectory(atPath: OpenMeteo.dataDirectory, withIntermediateDirectories: true)
defer { try? FileManager.default.removeItemIfExists(at: file)}
try run.measure("Compress to file, small chunks", 202) {
try OmFileWriter(dim0: data.count / 1024, dim1: 1024, chunk0: 8, chunk1: 128).write(file: file, compressionType: .pfor_delta2d_int16, scalefactor: 20, all: data, overwrite: true)
}
try run.measure("Decompress from file, small chunks", 46) {
let read = try OmFileReader(file: file)
return try read.readAll()
}*/
let exradTime = TimerangeDt(start: Timestamp(1900,1,1), to: Timestamp(2000,1,1), dtSeconds: 3600)
let exrad = run.measure("Calculate extra terrestrial radiation (100 years, hourly)", 47) {
return Zensun.extraTerrestrialRadiationBackwards(latitude: 52, longitude: 7, timerange: exradTime)
}
_ = run.measure("Interpolate radiation to 15 minutes", 246) {
let dtNew = exradTime.dtSeconds / 4
let timeNew = exradTime.range.add(-exradTime.dtSeconds + dtNew).range(dtSeconds: dtNew)
return exrad.interpolate(type: .solar_backwards_averaged, timeOld: exradTime, timeNew: timeNew, latitude: 52, longitude: 7, scalefactor: 100)
}
}
}
struct BenchmarkRun {
var timePerTest: Int
@discardableResult
func measure<T>(_ section: String, _ baseLineMeanMs: Double, fn: () throws -> T) rethrows -> T
{
print("| \(section.pad(80)) | ", terminator: "")
// Do not measure first execution
var result = try fn()
let start = DispatchTime.now()
let end = start.uptimeNanoseconds + UInt64(timePerTest) * 1_000_000_000
var min = 100.0
var max = 0.0
var count = 0
repeat {
let s = DispatchTime.now()
result = try fn()
count += 1
let elapsed = Double((DispatchTime.now().uptimeNanoseconds - s.uptimeNanoseconds)) / 1_000_000_000
if elapsed < min {
min = elapsed
}
if elapsed > max {
max = elapsed
}
} while DispatchTime.now().uptimeNanoseconds <= end
let elapsed = Double((DispatchTime.now().uptimeNanoseconds - start.uptimeNanoseconds)) / 1_000_000_000
let mean = elapsed / Double(count)
let diff = mean - baseLineMeanMs / 1000
let factor = round((mean)/(baseLineMeanMs / 1000)*100)/100
let b = "\(diff>0 ? "+" : "")\(diff.asSecondsPrettyPrint) (x\(factor))"
print("\(mean.asSecondsPrettyPrint.pad(8)) | \(min.asSecondsPrettyPrint.pad(8)) | \(max.asSecondsPrettyPrint.pad(8)) | \(String(count).pad(8)) | \(b.pad(20)) |")
return result
}
}
|