Skip to content
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

Merged
merged 21 commits into from
Oct 20, 2020

Conversation

PeterAdams-A
Copy link
Contributor

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.

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.
@linux-foundation-easycla
Copy link

linux-foundation-easycla bot commented Oct 13, 2020

CLA Check
The committers are authorized under a signed CLA.

@PeterAdams-A
Copy link
Contributor Author

Note: All the proto files in the Model directory are taken from grpc/grpc repo - all the swift to match is generated.

Copy link
Collaborator

@glbrntt glbrntt left a 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/Package.swift Outdated Show resolved Hide resolved
# Where products will be built; this is the SPM default.
GRPC_SWIFT_PATH:=../..
SWIFT_BUILD_PATH:=./.build
SWIFT_BUILD_CONFIGURATION=debug
Copy link
Collaborator

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

Comment on lines 16 to 18
SWIFT_BUILD_RELEASE:=${SWIFT} build ${SWIFT_FLAGS_RELEASE}
SWIFT_TEST:=${SWIFT} test ${SWIFT_FLAGS}
SWIFT_PACKAGE:=${SWIFT} package ${SWIFT_FLAGS}
Copy link
Collaborator

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

Copy link
Collaborator

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?

Copy link
Contributor Author

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.

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))
Copy link
Collaborator

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

SWIFT_PACKAGE:=${SWIFT} package ${SWIFT_FLAGS}

# Name of generated xcodeproj
XCODEPROJ:=GRPC.xcodeproj
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

And this one

Comment on lines 25 to 30
all:
${SWIFT_BUILD}

.PHONY:
plugins: ${PROTOC_GEN_SWIFT} ${PROTOC_GEN_GRPC_SWIFT}
cp $^ .
Copy link
Collaborator

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

@PeterAdams-A PeterAdams-A requested a review from glbrntt October 16, 2020 17:07
@PeterAdams-A
Copy link
Contributor Author

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?

Copy link
Collaborator

@glbrntt glbrntt left a 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.

Comment on lines 108 to 109
let allStopped = EventLoopFuture.andAllComplete(stoppedFutures, on: callbackLoop)
return allStopped.hop(to: callbackLoop).flatMap { _ in
Copy link
Collaborator

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?

Copy link
Contributor Author

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.

Comment on lines 153 to 154
while !self.stopRequested,
self.numberOfOutstandingRequests < self.maxPermittedOutstandingRequests {
Copy link
Collaborator

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()
Copy link
Collaborator

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?

Copy link
Contributor Author

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
Copy link
Collaborator

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.

Copy link
Collaborator

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

Copy link
Contributor Author

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.

Comment on lines 132 to 133
private var stopRequested = false
private var stopComplete: EventLoopPromise<Void>
Copy link
Collaborator

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.

Comment on lines +86 to +87
// Making a payload which is not compressable is hard - and not implemented in
// other implementations too.
Copy link
Collaborator

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(
Copy link
Collaborator

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)
Copy link
Collaborator

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?

Suggested change
return String(self.value)
return "\(self.value) ns"

Comment on lines 16 to 18
SWIFT_BUILD_RELEASE:=${SWIFT} build ${SWIFT_FLAGS_RELEASE}
SWIFT_TEST:=${SWIFT} test ${SWIFT_FLAGS}
SWIFT_PACKAGE:=${SWIFT} package ${SWIFT_FLAGS}
Copy link
Collaborator

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.")
}
}

Copy link
Collaborator

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
}())

@PeterAdams-A PeterAdams-A requested a review from glbrntt October 19, 2020 13:43
Copy link
Collaborator

@glbrntt glbrntt left a 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()
Copy link
Collaborator

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 {
Copy link
Collaborator

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)
}

@PeterAdams-A PeterAdams-A requested a review from glbrntt October 19, 2020 15:10
Copy link
Collaborator

@glbrntt glbrntt left a 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!

@glbrntt glbrntt added the semver/none No version bump required. label Oct 20, 2020
@glbrntt glbrntt merged commit 4f4d98a into grpc:main Oct 20, 2020
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
semver/none No version bump required.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants