-
Notifications
You must be signed in to change notification settings - Fork 419
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Basic Async Unary implementation of QPS benchmark #990
Conversation
Motivation: The main grpc repo contains a benchmarking suite which can be implemented in any language. This is an implementation of the async unary test and the framework surrounding it. Notes: * Benchmark so far only run on OSX, not linux * Benchmark is not optimised - there are currently allocations within the benchmark which could be removed. Modifications: Add framework for QPS style testing. Add implementation of Async Unary benchmark (without TLS) Add brief README describing how to run the benchmark Result: It's possible to run a benchmark of gRPC-swift which is in some way comparible to other languages.
|
Note: All the proto files in the Model directory are taken from grpc/grpc repo - all the swift to match is generated. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This looks mostly good. I've left a bunch of comments for tidying some stuff up. There are a couple of places where functions are quite long and have a lot of nesting which makes it pretty difficult to follow, I'd suggest splitting those up and also adding the odd blank link to break things up a bit. More documentation wouldn't go amiss either!
Also the formatting looks off in a bunch of places and is intended with 4 spaces and sometimes tabs. Running swiftformat .
in the root of the repo should fix things up.
Performance/QPSBenchmark/Sources/BenchmarkUtils/Histogram.swift
Outdated
Show resolved
Hide resolved
Performance/QPSBenchmark/Sources/BenchmarkUtils/Histogram.swift
Outdated
Show resolved
Hide resolved
Performance/QPSBenchmark/Sources/QPSBenchmark/Runtime/AsyncClient.swift
Outdated
Show resolved
Hide resolved
Performance/QPSBenchmark/Sources/QPSBenchmark/Runtime/ClientUtilities.swift
Outdated
Show resolved
Hide resolved
Performance/QPSBenchmark/Makefile
Outdated
# Where products will be built; this is the SPM default. | ||
GRPC_SWIFT_PATH:=../.. | ||
SWIFT_BUILD_PATH:=./.build | ||
SWIFT_BUILD_CONFIGURATION=debug |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
let's make this release
Performance/QPSBenchmark/Makefile
Outdated
SWIFT_BUILD_RELEASE:=${SWIFT} build ${SWIFT_FLAGS_RELEASE} | ||
SWIFT_TEST:=${SWIFT} test ${SWIFT_FLAGS} | ||
SWIFT_PACKAGE:=${SWIFT} package ${SWIFT_FLAGS} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Let's get rid of these too
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This one hasn't been addressed.
Although I'm also wondering whether we should just piggyback on top of the main Makefile
in the root of the repo to generate the protos. WDYT?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Its separate in the Package.swift - probably nice to keep the makefile separate too.
I've removed test and package.
Performance/QPSBenchmark/Makefile
Outdated
SWIFT_BUILD_CONFIGURATION=debug | ||
SWIFT_FLAGS=--build-path=${SWIFT_BUILD_PATH} --configuration=${SWIFT_BUILD_CONFIGURATION} | ||
# Force release configuration (for plugins) | ||
SWIFT_FLAGS_RELEASE=$(patsubst --configuration=%,--configuration=release,$(SWIFT_FLAGS)) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Let's get rid of this
Performance/QPSBenchmark/Makefile
Outdated
SWIFT_PACKAGE:=${SWIFT} package ${SWIFT_FLAGS} | ||
|
||
# Name of generated xcodeproj | ||
XCODEPROJ:=GRPC.xcodeproj |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
And this one
Performance/QPSBenchmark/Makefile
Outdated
all: | ||
${SWIFT_BUILD} | ||
|
||
.PHONY: | ||
plugins: ${PROTOC_GEN_SWIFT} ${PROTOC_GEN_GRPC_SWIFT} | ||
cp $^ . |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
we can get rid of these too, I think
Co-authored-by: George Barnett <[email protected]>
I've addressed most of your code review issues apart from the few things I've commented on above indicating I will not. I have added a tiny amount of extra docs as part of breaking up functions. Is there anything in particular you feel is definitely needing more doc? |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks for adding some more docs and splitting things up; the smaller functions help a lot.
This looks pretty good, I've left some comments mostly in AsyncClient
which I think we can simplify a little.
let allStopped = EventLoopFuture.andAllComplete(stoppedFutures, on: callbackLoop) | ||
return allStopped.hop(to: callbackLoop).flatMap { _ in |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Isn't allStopped
already on callbackLoop
-- that's what you're specifying in andAllComplete
no?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, looks like I over pulled fix from server side.
while !self.stopRequested, | ||
self.numberOfOutstandingRequests < self.maxPermittedOutstandingRequests { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
minor, but can we replace this and the check in makeRequestAndRepeat
? A computed property for "canMakeRequest" would avoid some duplication.
self.stopIsComplete() | ||
} else { | ||
// Try scheduling another request. | ||
try! self.launchRequests() |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can't we just call makeRequestAndRepeat
here since we're only replacing one RPC?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
In theory yes - it just felt nicer to make to perform a potential multiple request incase we've somehow lost one. I guess it costs a couple of compares so I'll change.
return self.stats.copyData(reset: reset) | ||
} | ||
|
||
private static func makeClientRequest(payloadConfig: Grpc_Testing_PayloadConfig) throws |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Does this need to be static
? Also since the config doesn't change per ChannelRepeater
why don't we just construct our request during init
? That way we can drop the throws
from launchRequests
, makeRequestAndRepeat
, and the try!
from requestCompleted
and start
. We also avoid having to construct the request each time.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
In fact, since AsyncQPSClient
throws
in its init
we can make the request there and hand it to the ChannelRepeater
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think having looked at it, that this would make us more similar to the C version too.
private var stopRequested = false | ||
private var stopComplete: EventLoopPromise<Void> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think these two would benefit from some documentation, stopping is a little unclear without reading all the code.
// Making a payload which is not compressable is hard - and not implemented in | ||
// other implementations too. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I guess they planned on doing this but never did and didn't want to break API 🤷♂️
} | ||
|
||
/// Create a client of the requested type. | ||
private static func createClient( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit: makeClient
extension Nanoseconds: CustomStringConvertible { | ||
/// Description to aid debugging. | ||
var description: String { | ||
return String(self.value) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should we include the units too?
return String(self.value) | |
return "\(self.value) ns" |
Performance/QPSBenchmark/Makefile
Outdated
SWIFT_BUILD_RELEASE:=${SWIFT} build ${SWIFT_FLAGS_RELEASE} | ||
SWIFT_TEST:=${SWIFT} test ${SWIFT_FLAGS} | ||
SWIFT_PACKAGE:=${SWIFT} package ${SWIFT_FLAGS} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This one hasn't been addressed.
Although I'm also wondering whether we should just piggyback on top of the main Makefile
in the root of the repo to generate the protos. WDYT?
logger.info("Worker has finished.") | ||
} | ||
} | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can we add in the following?
assert({
print("⚠️ WARNING: YOU ARE RUNNING IN DEBUG MODE ⚠️")
return true
}())
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looks great, just two nits remaining
@@ -192,7 +238,7 @@ final class AsyncUnaryQPSClient: QPSClient { | |||
self.stopIsComplete() | |||
} else { | |||
// Try scheduling another request. | |||
try! self.launchRequests() | |||
try! self.makeRequestAndRepeat() |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This function only throws
because of this try
on itself, let's drop both!
try self.makeRequestAndRepeat() | ||
} | ||
} | ||
|
||
/// Returns if it is permissible to make another request - ie we've not been asked to stop, and we're not at the limit of outstanding requests. | ||
private func canMakeRequest() -> Bool { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This should be a computed property:
private var canMakeRequest: Bool {
return !(self.stopRequested || self.numberOfOutstandingRequests >= self.maxPermittedOutstandingRequests)
}
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM thanks @PeterAdams-A!
Motivation:
The main grpc repo contains a benchmarking suite which can be implemented in any language.
This is an implementation of the async unary test and the framework surrounding it.
Notes:
could be removed.
Modifications:
Add framework for QPS style testing.
Add implementation of Async Unary benchmark (without TLS)
Add brief README describing how to run the benchmark
Result:
It's possible to run a benchmark of gRPC-swift which is in some way comparible to other
languages.