From ac2c9cc521391d77d9bf1d2a3fba6adcc52bec75 Mon Sep 17 00:00:00 2001 From: Michael Kirk Date: Wed, 12 Dec 2018 11:52:35 -0700 Subject: [PATCH 1/2] Benchmark Events by ID --- .../ConversationViewController.m | 2 + SignalMessaging/utils/Bench.swift | 66 +++++++++++++++++++ 2 files changed, 68 insertions(+) diff --git a/Signal/src/ViewControllers/ConversationView/ConversationViewController.m b/Signal/src/ViewControllers/ConversationView/ConversationViewController.m index 6a07dff52..e67c3d57e 100644 --- a/Signal/src/ViewControllers/ConversationView/ConversationViewController.m +++ b/Signal/src/ViewControllers/ConversationView/ConversationViewController.m @@ -3834,6 +3834,7 @@ typedef enum : NSUInteger { - (void)sendButtonPressed { + [BenchManager startEventWithTitle:@"Send message" eventId:@"message-send"]; [self tryToSendTextMessage:self.inputToolbar.messageText updateKeyboardState:YES]; } @@ -4539,6 +4540,7 @@ typedef enum : NSUInteger { if (scrollToBottom) { [self scrollToBottomAnimated:NO]; } + [BenchManager completeEventWithEventId:@"message-send"]; }]; } } @catch (NSException *exception) { diff --git a/SignalMessaging/utils/Bench.swift b/SignalMessaging/utils/Bench.swift index 06829833a..580ebb33b 100644 --- a/SignalMessaging/utils/Bench.swift +++ b/SignalMessaging/utils/Bench.swift @@ -28,3 +28,69 @@ public func Bench(title: String, block: () -> Void) { finish() } } + +/// When it's not convenient to retain the event completion handler, e.g. when the measured event +/// crosses multiple classes, you can use the BenchEvent tools +/// +/// // in one class +/// let message = getMessage() +/// BenchEventStart(title: "message sending", eventId: message.id) +/// +/// ... +/// +/// // in another class +/// BenchEventComplete(title: "message sending", eventId: message.id) +/// +/// Or in objc +/// +/// [BenchManager startEventWithTitle:"message sending" eventId:message.id] +/// ... +/// [BenchManager startEventWithTitle:"message sending" eventId:message.id] +public func BenchEventStart(title: String, eventId: BenchmarkEventId) { + BenchAsync(title: title) { finish in + runningEvents[eventId] = Event(title: title, eventId: eventId, completion: finish) + } +} + +public func BenchEventComplete(eventId: BenchmarkEventId) { + guard let event = runningEvents.removeValue(forKey: eventId) else { + Logger.debug("no active event with id: \(eventId)") + return + } + + event.completion() +} + +public typealias BenchmarkEventId = String + +private struct Event { + let title: String + let eventId: BenchmarkEventId + let completion: () -> Void +} + +private var runningEvents: [BenchmarkEventId: Event] = [:] + +@objc +public class BenchManager: NSObject { + + @objc + public class func startEvent(title: String, eventId: BenchmarkEventId) { + BenchEventStart(title: title, eventId: eventId) + } + + @objc + public class func completeEvent(eventId: BenchmarkEventId) { + BenchEventComplete(eventId: eventId) + } + + @objc + public class func benchAsync(title: String, block: (@escaping () -> Void) -> Void) { + BenchAsync(title: title, block: block) + } + + @objc + public class func bench(title: String, block: () -> Void) { + Bench(title: title, block: block) + } +} From 4b84583de8b1c54d814c2046b1bf6261510770e9 Mon Sep 17 00:00:00 2001 From: Michael Kirk Date: Thu, 13 Dec 2018 09:00:29 -0700 Subject: [PATCH 2/2] reload input bar async --- .../ConversationView/ConversationInputToolbar.m | 17 ++++++++++++----- 1 file changed, 12 insertions(+), 5 deletions(-) diff --git a/Signal/src/ViewControllers/ConversationView/ConversationInputToolbar.m b/Signal/src/ViewControllers/ConversationView/ConversationInputToolbar.m index a58049906..139785cdb 100644 --- a/Signal/src/ViewControllers/ConversationView/ConversationInputToolbar.m +++ b/Signal/src/ViewControllers/ConversationView/ConversationInputToolbar.m @@ -13,6 +13,7 @@ #import "UIView+OWS.h" #import "ViewControllerUtils.h" #import +#import #import #import @@ -221,11 +222,17 @@ const CGFloat kMaxTextViewHeight = 98; // Momentarily switch to a non-default keyboard, else reloadInputViews // will not affect the displayed keyboard. In practice this isn't perceptable to the user. // The alternative would be to dismiss-and-pop the keyboard, but that can cause a more pronounced animation. - self.inputTextView.keyboardType = UIKeyboardTypeNumbersAndPunctuation; - [self.inputTextView reloadInputViews]; - - self.inputTextView.keyboardType = UIKeyboardTypeDefault; - [self.inputTextView reloadInputViews]; + // + // This is surprisingly expensive (~5ms), so we do it async, *after* the message is rendered. + dispatch_async(dispatch_get_main_queue(), ^{ + [BenchManager benchWithTitle:@"toggleDefaultKeyboard" block:^{ + self.inputTextView.keyboardType = UIKeyboardTypeNumbersAndPunctuation; + [self.inputTextView reloadInputViews]; + + self.inputTextView.keyboardType = UIKeyboardTypeDefault; + [self.inputTextView reloadInputViews]; + }]; + }); } - (void)setQuotedReply:(nullable OWSQuotedReplyModel *)quotedReply