1/* 2 * 3 * Copyright 2019 gRPC authors. 4 * 5 * Licensed under the Apache License, Version 2.0 (the "License"); 6 * you may not use this file except in compliance with the License. 7 * You may obtain a copy of the License at 8 * 9 * http://www.apache.org/licenses/LICENSE-2.0 10 * 11 * Unless required by applicable law or agreed to in writing, software 12 * distributed under the License is distributed on an "AS IS" BASIS, 13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 * See the License for the specific language governing permissions and 15 * limitations under the License. 16 * 17 */ 18 19#import "PerfTests.h" 20 21#include <grpc/status.h> 22 23#import <GRPCClient/GRPCCall+ChannelArg.h> 24#import <GRPCClient/GRPCCall+Cronet.h> 25#import <GRPCClient/GRPCCall+Interceptor.h> 26#import <GRPCClient/GRPCCall+Tests.h> 27#import <GRPCClient/GRPCInterceptor.h> 28#import <GRPCClient/internal_testing/GRPCCall+InternalTests.h> 29#import <ProtoRPC/ProtoRPC.h> 30#import <RxLibrary/GRXBufferedPipe.h> 31#import <RxLibrary/GRXWriter+Immediate.h> 32#import <grpc/grpc.h> 33#import <grpc/support/log.h> 34#import "src/objective-c/tests/RemoteTestClient/Messages.pbobjc.h" 35#import "src/objective-c/tests/RemoteTestClient/Test.pbobjc.h" 36#import "src/objective-c/tests/RemoteTestClient/Test.pbrpc.h" 37 38#import "PerfTestsBlockCallbacks.h" 39 40#define TEST_TIMEOUT 128 41 42extern const char *kCFStreamVarName; 43 44// Convenience constructors for the generated proto messages: 45 46@interface RMTStreamingOutputCallRequest (Constructors) 47+ (instancetype)messageWithPayloadSize:(NSNumber *)payloadSize 48 requestedResponseSize:(NSNumber *)responseSize; 49@end 50 51@implementation RMTStreamingOutputCallRequest (Constructors) 52+ (instancetype)messageWithPayloadSize:(NSNumber *)payloadSize 53 requestedResponseSize:(NSNumber *)responseSize { 54 RMTStreamingOutputCallRequest *request = [self message]; 55 RMTResponseParameters *parameters = [RMTResponseParameters message]; 56 parameters.size = responseSize.intValue; 57 [request.responseParametersArray addObject:parameters]; 58 request.payload.body = [NSMutableData dataWithLength:payloadSize.unsignedIntegerValue]; 59 return request; 60} 61@end 62 63@interface DefaultInterceptorFactory : NSObject <GRPCInterceptorFactory> 64 65- (GRPCInterceptor *)createInterceptorWithManager:(GRPCInterceptorManager *)interceptorManager; 66 67@end 68 69@implementation DefaultInterceptorFactory 70 71- (GRPCInterceptor *)createInterceptorWithManager:(GRPCInterceptorManager *)interceptorManager { 72 dispatch_queue_t queue = dispatch_queue_create(NULL, DISPATCH_QUEUE_SERIAL); 73 return [[GRPCInterceptor alloc] initWithInterceptorManager:interceptorManager 74 dispatchQueue:queue]; 75} 76 77@end 78 79#pragma mark Tests 80 81@implementation PerfTests { 82 RMTTestService *_service; 83} 84 85+ (XCTestSuite *)defaultTestSuite { 86 if (self == [PerfTests class]) { 87 return [XCTestSuite testSuiteWithName:@"PerfTestsEmptySuite"]; 88 } else { 89 return super.defaultTestSuite; 90 } 91} 92 93+ (NSString *)host { 94 return nil; 95} 96 97// This number indicates how many bytes of overhead does Protocol Buffers encoding add onto the 98// message. The number varies as different message.proto is used on different servers. The actual 99// number for each interop server is overridden in corresponding derived test classes. 100- (int32_t)encodingOverhead { 101 return 0; 102} 103 104+ (GRPCTransportID)transport { 105 return NULL; 106} 107 108+ (NSString *)PEMRootCertificates { 109 return nil; 110} 111 112+ (NSString *)hostNameOverride { 113 return nil; 114} 115 116- (void)setUp { 117 self.continueAfterFailure = NO; 118 119 [GRPCCall resetHostSettings]; 120 121#pragma clang diagnostic push 122#pragma clang diagnostic ignored "-Wdeprecated-declarations" 123 [GRPCCall closeOpenConnections]; 124#pragma clang diagnostic pop 125 126 _service = [[self class] host] ? [RMTTestService serviceWithHost:[[self class] host]] : nil; 127} 128 129- (BOOL)isUsingCFStream { 130 return [NSStringFromClass([self class]) isEqualToString:@"PerfTestsCFStreamSSL"]; 131} 132 133- (void)pingPongV2APIWithRequest:(RMTStreamingOutputCallRequest *)request 134 numMessages:(int)numMessages 135 options:(GRPCMutableCallOptions *)options { 136 __weak XCTestExpectation *expectation = [self expectationWithDescription:@"PingPong"]; 137 138 __block BOOL flowControlEnabled = options.flowControlEnabled; 139 __block int index = 0; 140 __block GRPCStreamingProtoCall *call = [self->_service 141 fullDuplexCallWithResponseHandler:[[PerfTestsBlockCallbacks alloc] 142 initWithInitialMetadataCallback:nil 143 messageCallback:^(id message) { 144 int indexCopy; 145 @synchronized(self) { 146 indexCopy = index; 147 index += 1; 148 } 149 if (indexCopy < numMessages) { 150 [call writeMessage:request]; 151 if (flowControlEnabled) { 152 [call receiveNextMessage]; 153 } 154 } else { 155 [call finish]; 156 } 157 } 158 closeCallback:^(NSDictionary *trailingMetadata, 159 NSError *error) { 160 [expectation fulfill]; 161 }] 162 callOptions:options]; 163 [call start]; 164 if (flowControlEnabled) { 165 [call receiveNextMessage]; 166 } 167 [call writeMessage:request]; 168 [self waitForExpectationsWithTimeout:TEST_TIMEOUT handler:nil]; 169} 170 171- (void)testPingPongRPCWithV2API { 172 GRPCMutableCallOptions *options = [[GRPCMutableCallOptions alloc] init]; 173 options.transport = [[self class] transport]; 174 options.PEMRootCertificates = [[self class] PEMRootCertificates]; 175 options.hostNameOverride = [[self class] hostNameOverride]; 176 177 id request = [RMTStreamingOutputCallRequest messageWithPayloadSize:@1 requestedResponseSize:@1]; 178 179 // warm up 180 [self pingPongV2APIWithRequest:request numMessages:1000 options:options]; 181 182 [self measureBlock:^{ 183 [self pingPongV2APIWithRequest:request numMessages:1000 options:options]; 184 }]; 185} 186 187- (void)testPingPongRPCWithFlowControl { 188 GRPCMutableCallOptions *options = [[GRPCMutableCallOptions alloc] init]; 189 options.transport = [[self class] transport]; 190 options.PEMRootCertificates = [[self class] PEMRootCertificates]; 191 options.hostNameOverride = [[self class] hostNameOverride]; 192 options.flowControlEnabled = YES; 193 194 id request = [RMTStreamingOutputCallRequest messageWithPayloadSize:@1 requestedResponseSize:@1]; 195 196 // warm up 197 [self pingPongV2APIWithRequest:request numMessages:1000 options:options]; 198 199 [self measureBlock:^{ 200 [self pingPongV2APIWithRequest:request numMessages:1000 options:options]; 201 }]; 202} 203 204- (void)testPingPongRPCWithInterceptor { 205 GRPCMutableCallOptions *options = [[GRPCMutableCallOptions alloc] init]; 206 options.transport = [[self class] transport]; 207 options.PEMRootCertificates = [[self class] PEMRootCertificates]; 208 options.hostNameOverride = [[self class] hostNameOverride]; 209 options.interceptorFactories = @[ [[DefaultInterceptorFactory alloc] init] ]; 210 211 id request = [RMTStreamingOutputCallRequest messageWithPayloadSize:@1 requestedResponseSize:@1]; 212 213 // warm up 214 [self pingPongV2APIWithRequest:request numMessages:1000 options:options]; 215 216 [self measureBlock:^{ 217 [self pingPongV2APIWithRequest:request numMessages:1000 options:options]; 218 }]; 219} 220 221- (void)pingPongV1APIWithRequest:(RMTStreamingOutputCallRequest *)request 222 numMessages:(int)numMessages { 223 __block int index = 0; 224 __weak XCTestExpectation *expectation = [self expectationWithDescription:@"PingPong"]; 225 GRXBufferedPipe *requestsBuffer = [[GRXBufferedPipe alloc] init]; 226 227 [requestsBuffer writeValue:request]; 228 229 [_service fullDuplexCallWithRequestsWriter:requestsBuffer 230 eventHandler:^(BOOL done, RMTStreamingOutputCallResponse *response, 231 NSError *error) { 232 if (response) { 233 int indexCopy; 234 @synchronized(self) { 235 index += 1; 236 indexCopy = index; 237 } 238 if (indexCopy < numMessages) { 239 [requestsBuffer writeValue:request]; 240 } else { 241 [requestsBuffer writesFinishedWithError:nil]; 242 } 243 } 244 245 if (done) { 246 [expectation fulfill]; 247 } 248 }]; 249 [self waitForExpectationsWithTimeout:TEST_TIMEOUT handler:nil]; 250} 251 252- (void)testPingPongRPCWithV1API { 253 id request = [RMTStreamingOutputCallRequest messageWithPayloadSize:@1 requestedResponseSize:@1]; 254 [self pingPongV1APIWithRequest:request numMessages:1000]; 255 [self measureBlock:^{ 256 [self pingPongV1APIWithRequest:request numMessages:1000]; 257 }]; 258} 259 260- (void)unaryRPCsWithServices:(NSArray<RMTTestService *> *)services 261 request:(RMTSimpleRequest *)request 262 callsPerService:(int)callsPerService 263 maxOutstandingCalls:(int)maxOutstandingCalls 264 callOptions:(GRPCMutableCallOptions *)options { 265 __weak XCTestExpectation *expectation = [self expectationWithDescription:@"unaryRPC"]; 266 267 dispatch_semaphore_t sema = dispatch_semaphore_create(maxOutstandingCalls); 268 __block int index = 0; 269 270 for (RMTTestService *service in services) { 271 for (int i = 0; i < callsPerService; ++i) { 272 GRPCUnaryProtoCall *call = [service 273 unaryCallWithMessage:request 274 responseHandler:[[PerfTestsBlockCallbacks alloc] 275 initWithInitialMetadataCallback:nil 276 messageCallback:nil 277 closeCallback:^(NSDictionary *trailingMetadata, 278 NSError *error) { 279 dispatch_semaphore_signal(sema); 280 @synchronized(self) { 281 ++index; 282 if (index == 283 callsPerService * [services count]) { 284 [expectation fulfill]; 285 } 286 } 287 }] 288 callOptions:options]; 289 dispatch_time_t timeout = 290 dispatch_time(DISPATCH_TIME_NOW, (int64_t)(TEST_TIMEOUT * NSEC_PER_SEC)); 291 dispatch_semaphore_wait(sema, timeout); 292 [call start]; 293 } 294 } 295 296 [self waitForExpectationsWithTimeout:TEST_TIMEOUT handler:nil]; 297} 298 299- (void)test1MBUnaryRPC { 300 // Workaround Apple CFStream bug 301 if ([self isUsingCFStream]) { 302 return; 303 } 304 305 RMTSimpleRequest *request = [RMTSimpleRequest message]; 306 request.responseSize = 1048576; 307 request.payload.body = [NSMutableData dataWithLength:1048576]; 308 309 GRPCMutableCallOptions *options = [[GRPCMutableCallOptions alloc] init]; 310 options.transport = [[self class] transport]; 311 options.PEMRootCertificates = [[self class] PEMRootCertificates]; 312 options.hostNameOverride = [[self class] hostNameOverride]; 313 314 // warm up 315 [self unaryRPCsWithServices:@[ self->_service ] 316 request:request 317 callsPerService:50 318 maxOutstandingCalls:10 319 callOptions:options]; 320 321 [self measureBlock:^{ 322 [self unaryRPCsWithServices:@[ self->_service ] 323 request:request 324 callsPerService:50 325 maxOutstandingCalls:10 326 callOptions:options]; 327 }]; 328} 329 330- (void)test1KBUnaryRPC { 331 RMTSimpleRequest *request = [RMTSimpleRequest message]; 332 request.responseSize = 1024; 333 request.payload.body = [NSMutableData dataWithLength:1024]; 334 335 GRPCMutableCallOptions *options = [[GRPCMutableCallOptions alloc] init]; 336 options.transport = [[self class] transport]; 337 options.PEMRootCertificates = [[self class] PEMRootCertificates]; 338 options.hostNameOverride = [[self class] hostNameOverride]; 339 340 // warm up 341 [self unaryRPCsWithServices:@[ self->_service ] 342 request:request 343 callsPerService:1000 344 maxOutstandingCalls:100 345 callOptions:options]; 346 347 [self measureBlock:^{ 348 [self unaryRPCsWithServices:@[ self->_service ] 349 request:request 350 callsPerService:1000 351 maxOutstandingCalls:100 352 callOptions:options]; 353 }]; 354} 355 356- (void)testMultipleChannels { 357 NSString *port = [[[self class] host] componentsSeparatedByString:@":"][1]; 358 int kNumAddrs = 10; 359 NSMutableArray<NSString *> *addrs = [NSMutableArray arrayWithCapacity:kNumAddrs]; 360 NSMutableArray<RMTTestService *> *services = [NSMutableArray arrayWithCapacity:kNumAddrs]; 361 for (int i = 0; i < kNumAddrs; ++i) { 362 addrs[i] = [NSString stringWithFormat:@"127.0.0.%d", (i + 1)]; 363 NSString *hostWithPort = [NSString stringWithFormat:@"%@:%@", addrs[i], port]; 364 services[i] = [RMTTestService serviceWithHost:hostWithPort]; 365 } 366 367 RMTSimpleRequest *request = [RMTSimpleRequest message]; 368 request.responseSize = 0; 369 request.payload.body = [NSMutableData dataWithLength:0]; 370 371 GRPCMutableCallOptions *options = [[GRPCMutableCallOptions alloc] init]; 372 options.transport = [[self class] transport]; 373 options.PEMRootCertificates = [[self class] PEMRootCertificates]; 374 options.hostNameOverride = [[self class] hostNameOverride]; 375 376 // warm up 377 [self unaryRPCsWithServices:services 378 request:request 379 callsPerService:100 380 maxOutstandingCalls:100 381 callOptions:options]; 382 383 [self measureBlock:^{ 384 [self unaryRPCsWithServices:services 385 request:request 386 callsPerService:100 387 maxOutstandingCalls:100 388 callOptions:options]; 389 }]; 390} 391 392@end 393