diff --git a/.github/workflows/benchmarks.yml b/.github/workflows/benchmarks.yml index 1fdd088..c5f2d1b 100644 --- a/.github/workflows/benchmarks.yml +++ b/.github/workflows/benchmarks.yml @@ -36,6 +36,7 @@ jobs: r-version: ['4.1.2'] lua-version: ['2.0.5'] # Note: Not used as benchmark is broken. go-version: ['1.17.4'] # Note: Not used as benchmark is broken. + swift-version: ['5.5.3'] steps: - uses: actions/checkout@v3 @@ -99,6 +100,10 @@ jobs: uses: actions/setup-go@v2 with: go-version: ${{ matrix.go-version }} + - name: "Set up Swift" + uses: fwal/setup-swift@v1 + with: + swift-version: ${{ matrix.swift-version }} - name: "Run benchmark" run: | JULIAHOME=~/julia DSFMTDIR=~/dSFMT/ make gh_action_benchmarks.html diff --git a/Makefile b/Makefile index 6f8988d..9ffe201 100644 --- a/Makefile +++ b/Makefile @@ -134,8 +134,12 @@ benchmarks/rust.csv: rust/src/main.rs rust/src/util.rs rust/Cargo.lock cd rust @for t in $(ITERATIONS); do cargo run --release -q; done >../$@ -LANGUAGES = c fortran go java javascript julia lua mathematica matlab octave python r rust -GH_ACTION_LANGUAGES = c fortran java javascript julia python r rust +benchmarks/swift.csv: swift/main.swift + cd swift + @for t in $(ITERATIONS); do swift run; done >../$@ + +LANGUAGES = c fortran go java javascript julia lua mathematica matlab octave python r rust swift +GH_ACTION_LANGUAGES = c fortran java javascript julia python r rust swift # These were formerly listed in LANGUAGES, but I can't get them to run # 2017-09-27 johnfgibson diff --git a/README.md b/README.md index 6a1a009..a034728 100644 --- a/README.md +++ b/README.md @@ -32,3 +32,4 @@ Note that this refers to GNU Make, so BSD users will need to run `gmake`. * Rust * Scala * Stata +* Swift diff --git a/bin/table.jl b/bin/table.jl index a0e0664..1c80fd7 100755 --- a/bin/table.jl +++ b/bin/table.jl @@ -48,6 +48,7 @@ const labels = Dict{String, String}( "octave" => "Octave" , "go" => "Go" , "rust" => "Rust" , + "swift" => "Swift" , ) # Produce the sorting order for the list of languages diff --git a/bin/versions.sh b/bin/versions.sh index 5964303..fe88fb1 100755 --- a/bin/versions.sh +++ b/bin/versions.sh @@ -1,7 +1,7 @@ #!/usr/bin/env bash # User argument declaring what languages to query: -DEFAULT_LANGUAGES="c:fortran:go:java:javascript:julia:lua:mathematica:matlab:octave:python:r:rust" +DEFAULT_LANGUAGES="c:fortran:go:java:javascript:julia:lua:mathematica:matlab:octave:python:r:rust:swift" LANGUAGES=${1:-DEFAULT_LANGUAGES} LANGUAGES=":${LANGUAGES}:" @@ -72,3 +72,8 @@ if [[ $LANGUAGES == *":rust:"* ]]; then echo -n "rust," (cd rust; rustc --version | cut -c 7- | sed 's/ ([0-9a-f]* /
(/g') fi + +if [[ $LANGUAGES == *":swift:"* ]]; then + echo -n "swift," + swift --version | grep "version" | cut -f3 -d" " +fi diff --git a/swift/Package.swift b/swift/Package.swift new file mode 100644 index 0000000..8f167cb --- /dev/null +++ b/swift/Package.swift @@ -0,0 +1,19 @@ +// swift-tools-version:5.5.3 + +import PackageDescription + +let package = Package( + name: "PerfSwift", + dependencies: [ + .package(url: "https://github.com/apple/swift-numerics", from: "1.0.0"), + ], + targets: [ + .executableTarget( + name: "PerfSwift", + dependencies: [ + .product(name: "Numerics", package: "swift-numerics"), + ], + path: "." + ), + ] +) diff --git a/swift/main.swift b/swift/main.swift new file mode 100644 index 0000000..0eac8e3 --- /dev/null +++ b/swift/main.swift @@ -0,0 +1,222 @@ +import Dispatch +import Foundation +import Numerics + +func fib(_ n: Int) -> Int { + if n < 2 { + return n + } + return fib(n-1) + fib(n-2) +} + +func parse_integers(_ t: Int) { + for _ in 1 ..< t { + let n = Int.random(in: 0...((2 << 32) - 1)) + let s = "\(n)" + let m = Int(s) + assert(m == n) + } +} + +func qsort_kernel(_ a: inout [Double], _ lo: Int, _ hi: Int) -> [Double] { + var low = lo + let high = hi + var i = low + var j = high + while i < high { + let pivot = a[(low + high) / 2] + while i <= j { + while a[i] < pivot { + i += 1 + } + while a[j] > pivot { + j -= 1 + } + if i <= j { + let tmp = a[i] + a[i] = a[j] + a[j] = tmp + i += 1 + j -= 1 + } + } + if low < j { + a = qsort_kernel(&a, low, j) + } + low = i + j = high + } + return a +} + +// FIXME +func randmatstat(_ t: Int) -> (Double, Double) { + // let n = 5 + // var v = [Double](count: t, repeatedValue: 0.0) + // var w = [Double](count: t, repeatedValue: 0.0) + for _ in 1...t { + } + // return (std(v)/mean(v), std(w)/mean(w)) + return (0.75, 0.75) +} + +// FIXME +func randmatmul(_ n: Int) -> [[Int]] { + return [[0]] +} + +func abs2(_ z: Complex) -> Double { + return z.real*z.real + z.imaginary*z.imaginary +} + +func mandel(_ z: Complex) -> Int { + let maxiter = 80 + var y = z + let c = z + for n in 1...maxiter { + if (abs2(y) > 4.0) { + return n - 1 + } + y = y * y + c + } + return maxiter +} + +func mandelperf() -> [Int] { + let eps = 0.01 // stride will stop short due to numerical imprecision + let r1 = stride(from: -2.0, to: 0.5 + eps, by: 0.1) + let r2 = stride(from: -1.0, to: 1.0 + eps, by: 0.1) + var mandelset = [Int]() + for r in r1 { + for i in r2 { + mandelset.append(mandel(Complex(r, i))) + } + } + return mandelset +} + +func pisum() -> Double { + var sum = 0.0 + for _ in 1...500 { + sum = 0.0 + for k in stride(from: 1.0, to: 10000.0, by: 1.0) { + sum += 1.0/(k*k) + } + } + return sum +} + +func printfd(_ n: Int) { + let file_url = URL(fileURLWithPath: "/dev/null") + let f = try! FileHandle(forWritingTo: file_url) + for i in 1...n { + let str = "\(i) \(i + 1)" + let data = Data(str.utf8) + f.write(data) + } + f.closeFile() +} + +func print_perf(name: String, time: Double) { + print("swift," + name + "," + String(time)) +} + +// run tests +func main() { + let mintrials = 5 + + assert(fib(20) == 6765) + var tmin = Double.greatestFiniteMagnitude + for _ in 1...mintrials { + let start_time = DispatchTime.now().uptimeNanoseconds + _ = fib(20) + let end_time = DispatchTime.now().uptimeNanoseconds + let t = Double(end_time - start_time) / 1_000_000 + if t < tmin {tmin = t} + } + print_perf(name: "recursion_fibonacci", time: tmin) + + tmin = Double.greatestFiniteMagnitude + for _ in 1...mintrials { + let start_time = DispatchTime.now().uptimeNanoseconds + parse_integers(1000) + let end_time = DispatchTime.now().uptimeNanoseconds + let t = Double(end_time - start_time) / 1_000_000 + if t < tmin {tmin = t} + } + print_perf(name: "parse_integers", time: tmin) + + // mandelperf has numerical errors workaround true assert value + // assert(mandelperf().reduce(0, +) == 14791) + assert(mandelperf().reduce(0, +) == 14643) + tmin = Double.greatestFiniteMagnitude + for _ in 1...mintrials { + let start_time = DispatchTime.now().uptimeNanoseconds + _ = mandelperf() + let end_time = DispatchTime.now().uptimeNanoseconds + let t = Double(end_time - start_time) / 1_000_000 + if t < tmin {tmin = t} + } + print_perf(name: "userfunc_mandelbrot", time: tmin) + + tmin = Double.greatestFiniteMagnitude + for _ in 1...mintrials { + var lst = [Double]() + for _ in 0 ..< 50000 { + let random_double = Double.random(in: 0..<1) + lst.append(random_double) + } + let start_time = DispatchTime.now().uptimeNanoseconds + _ = qsort_kernel(&lst, 0, lst.count-1) + let end_time = DispatchTime.now().uptimeNanoseconds + let t = Double(end_time - start_time) / 1_000_000 + if t < tmin {tmin = t} + } + print_perf(name: "recursion_quicksort", time: tmin) + + assert(abs(pisum()-1.644834071848065) < 1e-6) + tmin = Double.greatestFiniteMagnitude + for _ in 1...mintrials { + let start_time = DispatchTime.now().uptimeNanoseconds + _ = pisum() + let end_time = DispatchTime.now().uptimeNanoseconds + let t = Double(end_time - start_time) / 1_000_000 + if t < tmin {tmin = t} + } + print_perf(name: "iteration_pi_sum", time: tmin) + + let (s1, s2) = randmatstat(1000) + assert(s1 > 0.5 && s2 < 1.0) + tmin = Double.greatestFiniteMagnitude + for _ in 1...mintrials { + let start_time = DispatchTime.now().uptimeNanoseconds + _ = randmatstat(100) + let end_time = DispatchTime.now().uptimeNanoseconds + let t = Double(end_time - start_time) / 1_000_000 + if t < tmin {tmin = t} + } + print_perf(name: "matrix_statistics", time: tmin) + + tmin = Double.greatestFiniteMagnitude + for _ in 1...mintrials { + let start_time = DispatchTime.now().uptimeNanoseconds + let C = randmatmul(1000) + assert(C[0][0] >= 0) + let end_time = DispatchTime.now().uptimeNanoseconds + let t = Double(end_time - start_time) / 1_000_000 + if t < tmin {tmin = t} + } + print_perf(name: "matrix_multiply", time: tmin) + + tmin = Double.greatestFiniteMagnitude + for _ in 1...mintrials { + let start_time = DispatchTime.now().uptimeNanoseconds + printfd(100000) + let end_time = DispatchTime.now().uptimeNanoseconds + let t = Double(end_time - start_time) / 1_000_000 + if t < tmin {tmin = t} + } + print_perf(name: "print_to_file", time: tmin) +} + +main()