Add baseline performance and allocation tests for scheduling tasks and executing (#2009)
### Motivation: In issue https://github.com/apple/swift-nio/issues/1316, we see a large number of allocations to happen when scheduling tasks. This can definitely be optimized. This PR adds a number of baseline allocation and performance tests for both `scheduleTask` and `execute`. In the next PRs, I am going to try a few optimizations to reduce the number of allocations. ### Modifications: Added baseline performance and allocation tests for `scheduleTask` and `execute`
This commit is contained in:
parent
094cd8c6e8
commit
213eb6887e
|
@ -16,20 +16,22 @@ import Dispatch
|
||||||
import NIOPosix
|
import NIOPosix
|
||||||
|
|
||||||
func run(identifier: String) {
|
func run(identifier: String) {
|
||||||
let group = MultiThreadedEventLoopGroup(numberOfThreads: 1)
|
|
||||||
let loop = group.next()
|
|
||||||
let dg = DispatchGroup()
|
|
||||||
|
|
||||||
measure(identifier: identifier) {
|
measure(identifier: identifier) {
|
||||||
loop.execute {
|
let group = MultiThreadedEventLoopGroup(numberOfThreads: 1)
|
||||||
for _ in 0..<10_000 {
|
let loop = group.next()
|
||||||
dg.enter()
|
let counter = try! loop.submit { () -> Int in
|
||||||
loop.scheduleTask(in: .nanoseconds(0)) { dg.leave() }
|
var counter: Int = 0
|
||||||
}
|
|
||||||
}
|
|
||||||
dg.wait()
|
|
||||||
return 10_000
|
|
||||||
}
|
|
||||||
|
|
||||||
try! group.syncShutdownGracefully()
|
for _ in 0..<10000 {
|
||||||
|
loop.scheduleTask(in: .hours(1)) {
|
||||||
|
counter &+= 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return counter
|
||||||
|
}.wait()
|
||||||
|
|
||||||
|
try! group.syncShutdownGracefully()
|
||||||
|
return counter
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,42 @@
|
||||||
|
//===----------------------------------------------------------------------===//
|
||||||
|
//
|
||||||
|
// This source file is part of the SwiftNIO open source project
|
||||||
|
//
|
||||||
|
// Copyright (c) 2021 Apple Inc. and the SwiftNIO project authors
|
||||||
|
// Licensed under Apache License v2.0
|
||||||
|
//
|
||||||
|
// See LICENSE.txt for license information
|
||||||
|
// See CONTRIBUTORS.txt for the list of SwiftNIO project authors
|
||||||
|
//
|
||||||
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
|
//
|
||||||
|
//===----------------------------------------------------------------------===//
|
||||||
|
|
||||||
|
import Dispatch
|
||||||
|
import NIOPosix
|
||||||
|
|
||||||
|
func run(identifier: String) {
|
||||||
|
let group = MultiThreadedEventLoopGroup(numberOfThreads: 1)
|
||||||
|
let loop = group.next()
|
||||||
|
let dg = DispatchGroup()
|
||||||
|
|
||||||
|
measure(identifier: identifier) {
|
||||||
|
var counter = 0
|
||||||
|
|
||||||
|
try! loop.submit {
|
||||||
|
for _ in 0..<10000 {
|
||||||
|
dg.enter()
|
||||||
|
|
||||||
|
loop.scheduleTask(in: .nanoseconds(0)) {
|
||||||
|
counter &+= 1
|
||||||
|
dg.leave()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}.wait()
|
||||||
|
dg.wait()
|
||||||
|
|
||||||
|
return counter
|
||||||
|
}
|
||||||
|
|
||||||
|
try! group.syncShutdownGracefully()
|
||||||
|
}
|
|
@ -0,0 +1,60 @@
|
||||||
|
//===----------------------------------------------------------------------===//
|
||||||
|
//
|
||||||
|
// This source file is part of the SwiftNIO open source project
|
||||||
|
//
|
||||||
|
// Copyright (c) 2021 Apple Inc. and the SwiftNIO project authors
|
||||||
|
// Licensed under Apache License v2.0
|
||||||
|
//
|
||||||
|
// See LICENSE.txt for license information
|
||||||
|
// See CONTRIBUTORS.txt for the list of SwiftNIO project authors
|
||||||
|
//
|
||||||
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
|
//
|
||||||
|
//===----------------------------------------------------------------------===//
|
||||||
|
|
||||||
|
import Foundation
|
||||||
|
import NIOCore
|
||||||
|
import NIOPosix
|
||||||
|
|
||||||
|
final class ExecuteBenchmark: Benchmark {
|
||||||
|
private var group: MultiThreadedEventLoopGroup!
|
||||||
|
private var loop: EventLoop!
|
||||||
|
private var dg: DispatchGroup!
|
||||||
|
private var counter = 0
|
||||||
|
|
||||||
|
func setUp() throws {
|
||||||
|
group = MultiThreadedEventLoopGroup(numberOfThreads: 1)
|
||||||
|
loop = group.next()
|
||||||
|
dg = DispatchGroup()
|
||||||
|
|
||||||
|
// We are preheating the EL to avoid growing the `ScheduledTask` `PriorityQueue`
|
||||||
|
// during the actual test
|
||||||
|
try! self.loop.submit {
|
||||||
|
var counter: Int = 0
|
||||||
|
for _ in 0..<100000 {
|
||||||
|
self.loop.scheduleTask(in: .nanoseconds(0)) {
|
||||||
|
counter &+= 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}.wait()
|
||||||
|
}
|
||||||
|
|
||||||
|
func tearDown() { }
|
||||||
|
|
||||||
|
func run() -> Int {
|
||||||
|
try! self.loop.submit {
|
||||||
|
for _ in 0..<10000 {
|
||||||
|
self.dg.enter()
|
||||||
|
|
||||||
|
self.loop.execute {
|
||||||
|
self.counter &+= 1
|
||||||
|
self.dg.leave()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}.wait()
|
||||||
|
self.dg.wait()
|
||||||
|
|
||||||
|
return self.counter
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,60 @@
|
||||||
|
//===----------------------------------------------------------------------===//
|
||||||
|
//
|
||||||
|
// This source file is part of the SwiftNIO open source project
|
||||||
|
//
|
||||||
|
// Copyright (c) 2021 Apple Inc. and the SwiftNIO project authors
|
||||||
|
// Licensed under Apache License v2.0
|
||||||
|
//
|
||||||
|
// See LICENSE.txt for license information
|
||||||
|
// See CONTRIBUTORS.txt for the list of SwiftNIO project authors
|
||||||
|
//
|
||||||
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
|
//
|
||||||
|
//===----------------------------------------------------------------------===//
|
||||||
|
|
||||||
|
import Foundation
|
||||||
|
import NIOCore
|
||||||
|
import NIOPosix
|
||||||
|
|
||||||
|
final class SchedulingAndRunningBenchmark: Benchmark {
|
||||||
|
private var group: MultiThreadedEventLoopGroup!
|
||||||
|
private var loop: EventLoop!
|
||||||
|
private var dg: DispatchGroup!
|
||||||
|
private var counter = 0
|
||||||
|
|
||||||
|
func setUp() throws {
|
||||||
|
group = MultiThreadedEventLoopGroup(numberOfThreads: 1)
|
||||||
|
loop = group.next()
|
||||||
|
dg = DispatchGroup()
|
||||||
|
|
||||||
|
// We are preheating the EL to avoid growing the `ScheduledTask` `PriorityQueue`
|
||||||
|
// during the actual test
|
||||||
|
try! self.loop.submit {
|
||||||
|
var counter: Int = 0
|
||||||
|
for _ in 0..<100000 {
|
||||||
|
self.loop.scheduleTask(in: .nanoseconds(0)) {
|
||||||
|
counter &+= 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}.wait()
|
||||||
|
}
|
||||||
|
|
||||||
|
func tearDown() { }
|
||||||
|
|
||||||
|
func run() -> Int {
|
||||||
|
try! self.loop.submit {
|
||||||
|
for _ in 0..<10000 {
|
||||||
|
self.dg.enter()
|
||||||
|
|
||||||
|
self.loop.scheduleTask(in: .nanoseconds(0)) {
|
||||||
|
self.counter &+= 1
|
||||||
|
self.dg.leave()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}.wait()
|
||||||
|
self.dg.wait()
|
||||||
|
|
||||||
|
return counter
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,56 @@
|
||||||
|
//===----------------------------------------------------------------------===//
|
||||||
|
//
|
||||||
|
// This source file is part of the SwiftNIO open source project
|
||||||
|
//
|
||||||
|
// Copyright (c) 2021 Apple Inc. and the SwiftNIO project authors
|
||||||
|
// Licensed under Apache License v2.0
|
||||||
|
//
|
||||||
|
// See LICENSE.txt for license information
|
||||||
|
// See CONTRIBUTORS.txt for the list of SwiftNIO project authors
|
||||||
|
//
|
||||||
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
|
//
|
||||||
|
//===----------------------------------------------------------------------===//
|
||||||
|
|
||||||
|
import Foundation
|
||||||
|
import NIOCore
|
||||||
|
import NIOPosix
|
||||||
|
|
||||||
|
final class SchedulingBenchmark: Benchmark {
|
||||||
|
private var group: MultiThreadedEventLoopGroup!
|
||||||
|
private var loop: EventLoop!
|
||||||
|
|
||||||
|
func setUp() throws {
|
||||||
|
group = MultiThreadedEventLoopGroup(numberOfThreads: 1)
|
||||||
|
loop = group.next()
|
||||||
|
|
||||||
|
// We are preheating the EL to avoid growing the `ScheduledTask` `PriorityQueue`
|
||||||
|
// during the actual test
|
||||||
|
try! self.loop.submit {
|
||||||
|
var counter: Int = 0
|
||||||
|
for _ in 0..<100000 {
|
||||||
|
self.loop.scheduleTask(in: .nanoseconds(0)) {
|
||||||
|
counter &+= 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}.wait()
|
||||||
|
}
|
||||||
|
|
||||||
|
func tearDown() { }
|
||||||
|
|
||||||
|
func run() -> Int {
|
||||||
|
let counter = try! self.loop.submit { () -> Int in
|
||||||
|
var counter: Int = 0
|
||||||
|
for _ in 0..<10000 {
|
||||||
|
self.loop.scheduleTask(in: .hours(1)) {
|
||||||
|
counter &+= 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return counter
|
||||||
|
}.wait()
|
||||||
|
|
||||||
|
return counter
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -833,4 +833,11 @@ try measureAndPrint(desc: "lock_4_threads_10M_ops",
|
||||||
try measureAndPrint(desc: "lock_8_threads_10M_ops",
|
try measureAndPrint(desc: "lock_8_threads_10M_ops",
|
||||||
benchmark: LockBenchmark(numberOfThreads: 8, lockOperationsPerThread: 1_250_000))
|
benchmark: LockBenchmark(numberOfThreads: 8, lockOperationsPerThread: 1_250_000))
|
||||||
|
|
||||||
|
try measureAndPrint(desc: "schedule_10000_tasks",
|
||||||
|
benchmark: SchedulingBenchmark())
|
||||||
|
|
||||||
|
try measureAndPrint(desc: "schedule_and_run_10000_tasks",
|
||||||
|
benchmark: SchedulingAndRunningBenchmark())
|
||||||
|
|
||||||
|
try measureAndPrint(desc: "execute_10000",
|
||||||
|
benchmark: ExecuteBenchmark())
|
||||||
|
|
|
@ -51,7 +51,8 @@ services:
|
||||||
- MAX_ALLOCS_ALLOWED_modifying_byte_buffer_view=2050
|
- MAX_ALLOCS_ALLOWED_modifying_byte_buffer_view=2050
|
||||||
- MAX_ALLOCS_ALLOWED_ping_pong_1000_reqs_1_conn=4400
|
- MAX_ALLOCS_ALLOWED_ping_pong_1000_reqs_1_conn=4400
|
||||||
- MAX_ALLOCS_ALLOWED_read_10000_chunks_from_file=190050
|
- MAX_ALLOCS_ALLOWED_read_10000_chunks_from_file=190050
|
||||||
- MAX_ALLOCS_ALLOWED_schedule_10000_tasks=90050
|
- MAX_ALLOCS_ALLOWED_schedule_10000_tasks=90150
|
||||||
|
- MAX_ALLOCS_ALLOWED_schedule_and_run_10000_tasks=100050
|
||||||
- MAX_ALLOCS_ALLOWED_scheduling_10000_executions=20150
|
- MAX_ALLOCS_ALLOWED_scheduling_10000_executions=20150
|
||||||
- MAX_ALLOCS_ALLOWED_udp_1000_reqs_1_conn=12200
|
- MAX_ALLOCS_ALLOWED_udp_1000_reqs_1_conn=12200
|
||||||
- MAX_ALLOCS_ALLOWED_udp_1_reqs_1000_conn=188050
|
- MAX_ALLOCS_ALLOWED_udp_1_reqs_1000_conn=188050
|
||||||
|
|
|
@ -51,7 +51,8 @@ services:
|
||||||
- MAX_ALLOCS_ALLOWED_modifying_byte_buffer_view=2050
|
- MAX_ALLOCS_ALLOWED_modifying_byte_buffer_view=2050
|
||||||
- MAX_ALLOCS_ALLOWED_ping_pong_1000_reqs_1_conn=4400
|
- MAX_ALLOCS_ALLOWED_ping_pong_1000_reqs_1_conn=4400
|
||||||
- MAX_ALLOCS_ALLOWED_read_10000_chunks_from_file=200050
|
- MAX_ALLOCS_ALLOWED_read_10000_chunks_from_file=200050
|
||||||
- MAX_ALLOCS_ALLOWED_schedule_10000_tasks=90050
|
- MAX_ALLOCS_ALLOWED_schedule_10000_tasks=90150
|
||||||
|
- MAX_ALLOCS_ALLOWED_schedule_and_run_10000_tasks=100050
|
||||||
- MAX_ALLOCS_ALLOWED_scheduling_10000_executions=20150
|
- MAX_ALLOCS_ALLOWED_scheduling_10000_executions=20150
|
||||||
- MAX_ALLOCS_ALLOWED_udp_1000_reqs_1_conn=12200
|
- MAX_ALLOCS_ALLOWED_udp_1000_reqs_1_conn=12200
|
||||||
- MAX_ALLOCS_ALLOWED_udp_1_reqs_1000_conn=188050
|
- MAX_ALLOCS_ALLOWED_udp_1_reqs_1000_conn=188050
|
||||||
|
|
|
@ -51,7 +51,8 @@ services:
|
||||||
- MAX_ALLOCS_ALLOWED_modifying_byte_buffer_view=2050
|
- MAX_ALLOCS_ALLOWED_modifying_byte_buffer_view=2050
|
||||||
- MAX_ALLOCS_ALLOWED_ping_pong_1000_reqs_1_conn=4400
|
- MAX_ALLOCS_ALLOWED_ping_pong_1000_reqs_1_conn=4400
|
||||||
- MAX_ALLOCS_ALLOWED_read_10000_chunks_from_file=190050
|
- MAX_ALLOCS_ALLOWED_read_10000_chunks_from_file=190050
|
||||||
- MAX_ALLOCS_ALLOWED_schedule_10000_tasks=90050
|
- MAX_ALLOCS_ALLOWED_schedule_10000_tasks=90150
|
||||||
|
- MAX_ALLOCS_ALLOWED_schedule_and_run_10000_tasks=100050
|
||||||
- MAX_ALLOCS_ALLOWED_scheduling_10000_executions=20150
|
- MAX_ALLOCS_ALLOWED_scheduling_10000_executions=20150
|
||||||
- MAX_ALLOCS_ALLOWED_udp_1000_reqs_1_conn=12200
|
- MAX_ALLOCS_ALLOWED_udp_1000_reqs_1_conn=12200
|
||||||
- MAX_ALLOCS_ALLOWED_udp_1_reqs_1000_conn=190050
|
- MAX_ALLOCS_ALLOWED_udp_1_reqs_1000_conn=190050
|
||||||
|
|
|
@ -50,7 +50,8 @@ services:
|
||||||
- MAX_ALLOCS_ALLOWED_modifying_byte_buffer_view=2050
|
- MAX_ALLOCS_ALLOWED_modifying_byte_buffer_view=2050
|
||||||
- MAX_ALLOCS_ALLOWED_ping_pong_1000_reqs_1_conn=4400
|
- MAX_ALLOCS_ALLOWED_ping_pong_1000_reqs_1_conn=4400
|
||||||
- MAX_ALLOCS_ALLOWED_read_10000_chunks_from_file=190050
|
- MAX_ALLOCS_ALLOWED_read_10000_chunks_from_file=190050
|
||||||
- MAX_ALLOCS_ALLOWED_schedule_10000_tasks=90050
|
- MAX_ALLOCS_ALLOWED_schedule_10000_tasks=90150
|
||||||
|
- MAX_ALLOCS_ALLOWED_schedule_and_run_10000_tasks=100050
|
||||||
- MAX_ALLOCS_ALLOWED_scheduling_10000_executions=20150
|
- MAX_ALLOCS_ALLOWED_scheduling_10000_executions=20150
|
||||||
- MAX_ALLOCS_ALLOWED_udp_1000_reqs_1_conn=12200
|
- MAX_ALLOCS_ALLOWED_udp_1000_reqs_1_conn=12200
|
||||||
- MAX_ALLOCS_ALLOWED_udp_1_reqs_1000_conn=190050
|
- MAX_ALLOCS_ALLOWED_udp_1_reqs_1000_conn=190050
|
||||||
|
|
|
@ -51,7 +51,8 @@ services:
|
||||||
- MAX_ALLOCS_ALLOWED_modifying_byte_buffer_view=2050
|
- MAX_ALLOCS_ALLOWED_modifying_byte_buffer_view=2050
|
||||||
- MAX_ALLOCS_ALLOWED_ping_pong_1000_reqs_1_conn=4400
|
- MAX_ALLOCS_ALLOWED_ping_pong_1000_reqs_1_conn=4400
|
||||||
- MAX_ALLOCS_ALLOWED_read_10000_chunks_from_file=160050
|
- MAX_ALLOCS_ALLOWED_read_10000_chunks_from_file=160050
|
||||||
- MAX_ALLOCS_ALLOWED_schedule_10000_tasks=90050
|
- MAX_ALLOCS_ALLOWED_schedule_10000_tasks=90150
|
||||||
|
- MAX_ALLOCS_ALLOWED_schedule_and_run_10000_tasks=100050
|
||||||
- MAX_ALLOCS_ALLOWED_scheduling_10000_executions=20150
|
- MAX_ALLOCS_ALLOWED_scheduling_10000_executions=20150
|
||||||
- MAX_ALLOCS_ALLOWED_udp_1000_reqs_1_conn=12200
|
- MAX_ALLOCS_ALLOWED_udp_1000_reqs_1_conn=12200
|
||||||
- MAX_ALLOCS_ALLOWED_udp_1_reqs_1000_conn=188050
|
- MAX_ALLOCS_ALLOWED_udp_1_reqs_1000_conn=188050
|
||||||
|
|
Loading…
Reference in New Issue