Update for BatchRequest and PlanetsRequest for modern concurrency (#11)

This commit is contained in:
Vincent Smithers 2021-11-24 10:43:28 -05:00 committed by GitHub
parent f512a9fafe
commit 448c495948
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 129 additions and 40 deletions

View File

@ -11,7 +11,7 @@
<key>com.apple.XCTPerformanceMetric_WallClockTime</key>
<dict>
<key>baselineAverage</key>
<real>1.4072</real>
<real>1.407200</real>
<key>baselineIntegrationDisplayName</key>
<string>Local Baseline</string>
</dict>
@ -21,7 +21,7 @@
<key>com.apple.XCTPerformanceMetric_WallClockTime</key>
<dict>
<key>baselineAverage</key>
<real>0.0048445</real>
<real>0.004844</real>
<key>baselineIntegrationDisplayName</key>
<string>Local Baseline</string>
</dict>
@ -31,7 +31,7 @@
<key>com.apple.XCTPerformanceMetric_WallClockTime</key>
<dict>
<key>baselineAverage</key>
<real>2.55</real>
<real>2.550000</real>
<key>baselineIntegrationDisplayName</key>
<string>Local Baseline</string>
</dict>
@ -41,7 +41,7 @@
<key>com.apple.XCTPerformanceMetric_WallClockTime</key>
<dict>
<key>baselineAverage</key>
<real>0.45805</real>
<real>0.458050</real>
<key>baselineIntegrationDisplayName</key>
<string>Local Baseline</string>
</dict>
@ -51,7 +51,7 @@
<key>com.apple.XCTPerformanceMetric_WallClockTime</key>
<dict>
<key>baselineAverage</key>
<real>0.1499</real>
<real>0.149900</real>
<key>baselineIntegrationDisplayName</key>
<string>Local Baseline</string>
</dict>
@ -61,7 +61,7 @@
<key>com.apple.XCTPerformanceMetric_WallClockTime</key>
<dict>
<key>baselineAverage</key>
<real>0.28996</real>
<real>0.289960</real>
<key>baselineIntegrationDisplayName</key>
<string>Local Baseline</string>
</dict>
@ -71,7 +71,7 @@
<key>com.apple.XCTPerformanceMetric_WallClockTime</key>
<dict>
<key>baselineAverage</key>
<real>0.2513</real>
<real>0.251300</real>
<key>baselineIntegrationDisplayName</key>
<string>Local Baseline</string>
</dict>
@ -94,7 +94,7 @@
<key>com.apple.XCTPerformanceMetric_WallClockTime</key>
<dict>
<key>baselineAverage</key>
<real>0.14739</real>
<real>0.147390</real>
<key>baselineIntegrationDisplayName</key>
<string>Local Baseline</string>
</dict>
@ -104,7 +104,7 @@
<key>com.apple.XCTPerformanceMetric_WallClockTime</key>
<dict>
<key>baselineAverage</key>
<real>0.766</real>
<real>0.766000</real>
<key>baselineIntegrationDisplayName</key>
<string>Local Baseline</string>
</dict>

View File

@ -0,0 +1,32 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>classNames</key>
<dict>
<key>PerformanceTests</key>
<dict>
<key>testBatchRequestPlanetCoordinates()</key>
<dict>
<key>com.apple.XCTPerformanceMetric_WallClockTime</key>
<dict>
<key>baselineAverage</key>
<real>0.000011</real>
<key>baselineIntegrationDisplayName</key>
<string>Local Baseline</string>
</dict>
</dict>
<key>testBatchRequestPlanetCoordinatesDeprecated()</key>
<dict>
<key>com.apple.XCTPerformanceMetric_WallClockTime</key>
<dict>
<key>baselineAverage</key>
<real>0.637282</real>
<key>baselineIntegrationDisplayName</key>
<string>Local Baseline</string>
</dict>
</dict>
</dict>
</dict>
</dict>
</plist>

View File

@ -28,6 +28,30 @@
<key>targetArchitecture</key>
<string>x86_64</string>
</dict>
<key>ECF89B48-B02C-4625-9453-0816115FCFF4</key>
<dict>
<key>localComputer</key>
<dict>
<key>busSpeedInMHz</key>
<integer>0</integer>
<key>cpuCount</key>
<integer>1</integer>
<key>cpuKind</key>
<string>Apple M1</string>
<key>cpuSpeedInMHz</key>
<integer>0</integer>
<key>logicalCPUCoresPerPackage</key>
<integer>8</integer>
<key>modelCode</key>
<string>MacBookPro17,1</string>
<key>physicalCPUCoresPerPackage</key>
<integer>8</integer>
<key>platformIdentifier</key>
<string>com.apple.platform.macosx</string>
</dict>
<key>targetArchitecture</key>
<string>arm64</string>
</dict>
</dict>
</dict>
</plist>

View File

@ -6,7 +6,7 @@ import PackageDescription
let package = Package(
name: "SwissEphemeris",
platforms: [
.macOS(.v10_15), .iOS(.v14)
.macOS(.v11), .iOS(.v14)
],
products: [
// Products define the executables and libraries produced by a package, and make them visible to other packages.

View File

@ -106,19 +106,16 @@ The `enum` types for `Planet`, `Asteroid`, and `LunarNode` correspond to IPL num
#### Batch Calculations
To get the most out of the ephemeris, you may need to make a lot of calculations at one time. Making calculations in mass is an expensive operation. and should never be done on the main thread. If you are making hundreds of calculations at one time it is recommended to use a `BatchRequest` for increased performance and to avoid the undefined behavior that results from making numerous calculations concurrently on a single thread.
To get the most out of the ephemeris, you may need to make a lot of calculations at one time. Making calculations in mass is an expensive operation. and should never be done on the main thread. If you are making hundreds of calculations at one time it is recommended to use a `BatchRequest` for increased performance and to avoid the undefined behavior that results from making a high number of calculations concurrently.
For example, if I wanted to calculate the exact dates for the phases of the moon I could create a map of the hourly percentage by requesting a `Lunation` for every hour over the next 30 days.
For example, to calculate the exact coordinates for the sun for a period of time:
```swift
let lunationsRequest = LunationsRequest()
let request = PlanetsRequest(body: .sun)
let now = Date()
let end = Date(),.addingTimeInterval(60 * 60 * 24 * 30)
lunationsRequest.fetch(start: now, end: end, interval: 60.0 * 60.0) { lunations in
/// An array of `Lunation` for every hour between now and 720 hours in the future
/// mapped to percentage values.
let percentages = lunations.map { $0.percentage }
}
let end = now.addingTimeInterval(60 * 60 * 24 * 30)
// Asynchronously returns an array of `.sun` `Coordinate`s for every hour between now and 720 hours in the future.
let batchCoordinates = await request.fetch(start: now, end: end, interval: 60.0 * 60.0)
```
### Testing

View File

@ -10,14 +10,24 @@ import Foundation
/// Models a request for batch calculations at a consistent
/// date interval. If you are making hundreds of calculations at one time
/// it is recommended to use these utility methods for increased performance
/// and to avoid the undefined behavior that results from making numerous calculations
/// concurrently on a single thread.
/// and to avoid the undefined behavior that results from making a high number of
/// calculations concurrently.
public protocol BatchRequest {
/// The type returned from the fetch request.
associatedtype EphemerisItem
/// The maximum amount of concurrent calculations. Exceed `478` at your own risk.
var datesThreshold: Int { get }
@available(macOS 12.0.0, *)
/// Fetches a collection of `EphemerisItem` for a time interval through a span of dates.
/// - Parameters:
/// - start: The beginning of the date range.
/// - end: The end of the date range.
/// - interval: The frequency in which an item is calculated.
/// - Returns: An array of `EphemerisItem`.
func fetch(start: Date, end: Date, interval: TimeInterval) async -> [EphemerisItem]
@available(*, deprecated, renamed: "fetch(start:end:interval:_:)")
/// Fetches all of the `RequestType`s between two dates.
/// - Parameters:
/// - start: The starting date.

View File

@ -20,7 +20,28 @@ final public class PlanetsRequest: BatchRequest {
public init(body: Planet) {
self.body = body
}
@available(macOS 12.0.0, *)
public func fetch(start: Date, end: Date, interval: TimeInterval = 60.0) async -> [EphemerisItem] {
var coordinates = [EphemerisItem]()
var dates = dates(for: start, end: end, interval: interval)
let stream = AsyncStream<[EphemerisItem]> {
guard !dates.isEmpty else { return nil }
do {
try await Task.sleep(nanoseconds: 1)
} catch {
return nil
}
let batch = dates.removeFirst()
return batch.map { EphemerisItem(body: self.body, date: $0) }
}
for await items in stream {
coordinates.append(contentsOf: items)
}
return coordinates
}
@available(*, deprecated, renamed: "fetch(start:end:interval:_:)")
public func fetch(start: Date, end: Date, interval: TimeInterval = 60.0, _ closure: ([EphemerisItem]) -> Void) {
var coordinates = [EphemerisItem]()
let group = DispatchGroup()

View File

@ -122,24 +122,29 @@ final class PerformanceTests: XCTestCase {
}
}
func testBatchRequestPlanetCoordinates() {
let planetsRequest = PlanetsRequest(body: .moon)
measure {
planetsRequest.fetch(start: date, end: date.addingTimeInterval(60 * 60 * 24 * 30)) {
XCTAssertEqual($0.count, 43200)
}
}
}
func testBatchRequestPlanetCoordinatesDeprecated() {
measure {
PlanetsRequest(body: .moon).fetch(start: date, end: date.addingTimeInterval(60 * 60 * 24 * 30)) {
XCTAssertEqual($0.count, 43200)
}
}
}
@available(macOS 12.0.0, *)
func testBatchRequestPlanetCoordinates() async {
let batch = await PlanetsRequest(body: .moon).fetch(start: date, end: date.addingTimeInterval(60 * 60 * 24 * 30))
XCTAssertEqual(batch.count, 43200)
}
static var allTests = [
("testCoordinatePerformance",testCoordinatePerformance,
"testHouseCuspsPerformance", testHouseCuspsPerformance,
"testRiseTimePerfomance", testRiseTimePerfomance,
"testSetTimePerformance", testSetTimePerformance,
"testLunationPerformance", testLunationPerformance,
"testAspectPerformance", testAspectPerformance,
"testSpringEquinoxDatePerformance", testSpringEquinoxDatePerformance,
"testAutumnalEquinoxDatePerformance", testAutumnalEquinoxDatePerformance,
"testBatchRequestPlanetCoordinates", testBatchRequestPlanetCoordinates)
]
static var allTests = [
("testCoordinatePerformance",testCoordinatePerformance,
"testHouseCuspsPerformance", testHouseCuspsPerformance,
"testRiseTimePerfomance", testRiseTimePerfomance,
"testSetTimePerformance", testSetTimePerformance,
"testLunationPerformance", testLunationPerformance,
"testAspectPerformance", testAspectPerformance,
"testSpringEquinoxDatePerformance", testSpringEquinoxDatePerformance,
"testAutumnalEquinoxDatePerformance", testAutumnalEquinoxDatePerformance,
"testBatchRequestPlanetCoordinatesDeprecated", testBatchRequestPlanetCoordinatesDeprecated)
]
}