Created
November 10, 2019 21:18
-
-
Save michaeleisel/d3968bf00b7fe519088c344221222807 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
//Copyright (c) 2018 Michael Eisel. All rights reserved. | |
import Foundation | |
import QuartzCore | |
// import IkigaJSON | |
import ZippyJSON | |
import os | |
func dataFromFile(_ file: String) -> Data { | |
return try! String(contentsOfFile: Bundle.main.path(forResource: file, ofType: nil)!).data(using: .utf8)! | |
} | |
var shouldContinue = true | |
func standardDeviation(_ arr : [CFTimeInterval]) -> CFTimeInterval { | |
let length = CFTimeInterval(arr.count) | |
let avg = arr.reduce(0, {$0 + $1}) / length | |
let sumOfSquaredAvgDiff = arr.map { pow($0 - avg, 2.0)}.reduce(0, {$0 + $1}) | |
return sqrt(sumOfSquaredAvgDiff / length) | |
} | |
func threadTime() -> CFTimeInterval { | |
var tp: timespec = timespec() | |
if #available(macOS 10.12, *) { | |
clock_gettime(CLOCK_THREAD_CPUTIME_ID, &tp) | |
} else { | |
abort() | |
} | |
return Double(tp.tv_sec) + Double(tp.tv_nsec) / 1e9; | |
} | |
func time(_ closure: () -> ()) -> CFTimeInterval { | |
let start = threadTime() | |
let _: Int = autoreleasepool { | |
closure() | |
return 0 | |
} | |
let end = threadTime() | |
return end - start | |
} | |
func prettify(_ time: CFTimeInterval) -> String { | |
return String(format: "%.2e", time) | |
} | |
func profile(runAfter: Bool, testClosure: () -> (), appleClosure: () -> (), otherClosure: () -> ()) { | |
/*print("Ikiga") | |
profile(runAfter: false, otherClosure)*/ | |
print("Apple") | |
profile(runAfter: false, appleClosure) | |
print("Jaunt") | |
profile(runAfter: false, testClosure) | |
} | |
func pp(_ time: CFTimeInterval) -> String { | |
return String(format: "%.2e", time) | |
} | |
func p(_ times: [CFTimeInterval], _ percent: Double) -> CFTimeInterval { | |
let idx = Int(Double(times.count) * percent / 100) | |
return times.sorted()[idx] | |
} | |
func summary(_ times: [CFTimeInterval]) { | |
let average = times.reduce(+)! / CFTimeInterval(times.count) | |
print("\(pp(average)), \(pp(p(times, 25))), \(pp(p(times, 50))), \(pp(p(times, 75))), standard deviation: \(String(format: "%.02lf", standardDeviation(times) / average * 100))%, min: \(pp(times.min()!)), max: \(pp(times.max()!))") | |
} | |
var allTimes: [CFTimeInterval] = [] | |
func profile(runAfter: Bool, _ closure: () -> ()) { | |
var times: [CFTimeInterval] = [] | |
for _ in 0..<50 { | |
times.append(time(closure)) | |
} | |
allTimes += times | |
// print(times) | |
summary(times) | |
if runAfter { | |
for _ in 0..<500 { | |
closure() | |
} | |
} | |
} | |
var twitterData = dataFromFile("twitter.json") | |
var canadaData = dataFromFile("canada.json") | |
var githubData = dataFromFile("github_events.json") | |
var apacheData = dataFromFile("apache_builds.json") | |
// var data = canadaData | |
var data = twitterData | |
// var data = githubData | |
// var data = apacheData | |
typealias ToDecode = Twitter | |
// typealias ToDecode = Canada | |
// typealias ToDecode = ghEvents | |
// typealias ToDecode = ApacheBuilds | |
/*let iDecoder = IkigaJSONDecoder() | |
//iDecoder.settings.keyDecodingStrategy = .convertFromSnakeCase | |
let ikigaBlock = {() -> TwitterPayload in | |
return try! iDecoder.decode(TwitterPayload.self, from: data) | |
}*/ | |
let testDecoder = ZippyJSONDecoder() | |
let testBlock = {() -> ToDecode in | |
return try! testDecoder.decode(ToDecode.self, from: data) | |
} | |
let appleDecoder = Foundation.JSONDecoder() | |
//appleDecoder.keyDecodingStrategy = .convertFromSnakeCase | |
let appleBlock = {() -> ToDecode in | |
return try! appleDecoder.decode(ToDecode.self, from: data) | |
} | |
func eq(_ o1: Any, _ o2: Any) { | |
if let array1 = o1 as? Array<Any>, let array2 = o2 as? Array<Any> { | |
assert(array1.count == array2.count) | |
for (element1, element2) in array1.zzip(array2) { | |
eq(element1, element2) | |
} | |
} else if let dictionary1 = o1 as? Dictionary<String, Any>, let dictionary2 = o2 as? Dictionary<String, Any> { | |
assert(dictionary1.count == dictionary2.count) | |
for key in dictionary1.keys.sorted() { | |
eq(dictionary1[key]!, dictionary2[key]!) | |
} | |
} else if let string1 = o1 as? String, let string2 = o2 as? String { | |
assert(string1 == string2) | |
} else if let int1 = o1 as? Int, let int2 = o2 as? Int { | |
assert(int1 == int2) | |
} else if let null1 = o1 as? NSNull, let null2 = o2 as? NSNull { | |
assert(null1 == null2) | |
} else if let bool1 = o1 as? Bool, let bool2 = o2 as? Bool { | |
assert(bool1 == bool2) | |
} else if let d1 = o1 as? Double, let d2 = o2 as? Double { | |
assert(d1 == d2) | |
} else { | |
} | |
} | |
func test<T: Equatable>(_ a: [T], _ b: [T]) { | |
if let zz = a.zzip(b).first(where: { (x, y) in x != y }) { | |
print("\(zz)") | |
abort() | |
} | |
} | |
func runTests() { | |
let a = testBlock() | |
let b = appleBlock() | |
assert(a == b) | |
} | |
extension DecodingError: Equatable { | |
public static func == (lhs: DecodingError, rhs: DecodingError) -> Bool { | |
switch lhs { | |
case .typeMismatch(let lType, let lContext): | |
if case let DecodingError.typeMismatch(rType, rContext) = rhs { | |
return lType == rType && rContext == lContext | |
} | |
case .valueNotFound(let lType, let lContext): | |
if case let DecodingError.valueNotFound(rType, rContext) = rhs { | |
return lType == rType && rContext == lContext | |
} | |
case .keyNotFound(let lKey, let lContext): | |
if case let DecodingError.keyNotFound(rKey, rContext) = rhs { | |
return keysEqual(lKey, rKey) && rContext == lContext | |
} | |
case .dataCorrupted(let lContext): | |
if case let DecodingError.dataCorrupted(rContext) = rhs { | |
return rContext == lContext | |
} | |
@unknown default: | |
return false | |
} | |
return false | |
} | |
} | |
func keysEqual(_ lhs: CodingKey, _ rhs: CodingKey) -> Bool { | |
return lhs.stringValue == rhs.stringValue || (lhs.intValue != nil && lhs.intValue == rhs.intValue) | |
} | |
fileprivate struct JSONKey : CodingKey { | |
public var stringValue: String | |
public var intValue: Int? | |
public init?(stringValue: String) { | |
self.stringValue = stringValue | |
self.intValue = nil | |
} | |
public init?(intValue: Int) { | |
self.stringValue = "\(intValue)" | |
self.intValue = intValue | |
} | |
public init(stringValue: String, intValue: Int?) { | |
self.stringValue = stringValue | |
self.intValue = intValue | |
} | |
fileprivate init(index: Int) { | |
self.stringValue = "Index \(index)" | |
self.intValue = index | |
} | |
fileprivate static let `super` = JSONKey(stringValue: "super")! | |
} | |
extension DecodingError.Context: Equatable { | |
public static func == (lhs: DecodingError.Context, rhs: DecodingError.Context) -> Bool { | |
let pathsEqual = lhs.codingPath.count == rhs.codingPath.count && zip(lhs.codingPath, rhs.codingPath).allSatisfy { (a, b) in | |
keysEqual(a, b) | |
} | |
return pathsEqual && lhs.debugDescription == rhs.debugDescription | |
} | |
} | |
public func benchmark() { | |
//testDecoder.keyDecodingStrategy = .convertFromSnakeCase | |
//appleDecoder.keyDecodingStrategy = .convertFromSnakeCase | |
if #available(OSX 10.12, *) { | |
testDecoder.dateDecodingStrategy = .iso8601 | |
appleDecoder.dateDecodingStrategy = .iso8601 | |
} | |
assert(testBlock() == appleBlock()) | |
for _ in 0..<30 { | |
profile(runAfter: true, testClosure: { | |
testBlock() | |
}, appleClosure: { | |
appleBlock() | |
}, otherClosure: { | |
}) | |
} | |
summary(allTimes) | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment