Successfully reported this slideshow.
We use your LinkedIn profile and activity data to personalize ads and to show you more relevant ads. You can change your ad preferences anytime.

Оптимизация UI потока / Дмитрий Куркин (Mail.Ru)

579 views

Published on

- WatchDog. Что это такое? Схема реализации с таймером и схема реализации на RunLoop'e.
- Как собирать результат работы WatchDog. Примеры того, что можно таким образом найти, и что мы нашли на проекте ICQ.
- Как работать с полученными результатами. Анализ стеков потоков. Профилирование с учетом расходов на синхронизацию.
- Проблемы при работе с WatchDog. Системные механизмы и популярные библиотеки, вызывающие проблемы. Методы обхода этих проблем. Итоговые параметры, применяемые на проекте ICQ.

Published in: Engineering
  • Be the first to comment

  • Be the first to like this

Оптимизация UI потока / Дмитрий Куркин (Mail.Ru)

  1. 1. Оптимизация UI потока Куркин Дмитрий
  2. 2. Беда - UI тормозит • iOS приложение тоже может тормозить • У вас все отлично – у тестировщика тормозит • Пользователь хочет написать, а приложение занято своими делами • У всех все отлично, но 30% неведомых пользователей жалуются на тормоза
  3. 3. Как систематически решать проблему тормозов? • Тормоза UI – это когда главный поток чем-то занят и не может 60 раз в секунду отдавать картинку приложения • Нужно ловить процедуры, которые занимают главный поток слишком долго
  4. 4. WatchDog WatchDog – это процесс или поток, который следит за другим процессом или потоком
  5. 5. Схема реализации с опросом UI потока Ping Ping UI thread WatchDog threadЗамер итерации Превышение временного лимита https://blog.testmunk.com/why-the-facebook-app-is-rated-below-2-stars/ https://github.com/wojteklu/Watchdog
  6. 6. Схема реализации на итерации RunLoop WatchDog thread UI thread Run Loop Замер итерации Превышение временного лимита https://gist.github.com/jspahrsummers/419266f5231832602bec
  7. 7. Сбор результатов
  8. 8. Логировать превышение лимита времени NSLog(@"%@: iteration of run loop %p took %.f ms to execute", self, self.runLoop, (double)duration * 1000); NSLog(@"WATCHDOG: task took longer than %d seconds", self.timeout); Main thread was blocked for 1.25s + Можно собирать проблемы без остановки приложения − Сложно понять, что является источником проблемы
  9. 9. Кидать исключение для создания крэшлога Exception Type: SIGABRT Exception Codes: #0 at 0x383bc1f0 Crashed Thread: 20 Thread 0: 0 libsystem... semaphore_wait_trap + 8 1 TCC TCCAccessPreflight + 210 2 AddressBook ABCDBContextCreateWithPathAndAddressBook + 70 3 AddressBook ABCCreateAddressBookWithDatabaseDirectoryA... 4 AddressBook ABAddressBookCreateWithDatabaseDirectory + 72 5 AddressBook ABAddressBookCreateWithOptionsAndPolicy + 144 6 AddressBook ABAddressBookCreateWithOptions + 86 7 ICQ +[AddressBookManager newAddressBook] ... 16 Graphics... GSEventRunModal + 136 17 UIKit UIApplicationMain + 1134 18 ICQ main (main.mm:23) 19 libdyld.dylibstart + 0 + Можно смотреть стек главного потока – очень высока вероятность, что там будет видна причина проблемы. − Приложение останавливается. Пользователь вынужден начинать все с начала.
  10. 10. Как работать с результатами
  11. 11. Анализ стека в крэшлоге Thread 0: 0 libsyste...semaphore_wait_trap + 8 1 libxpc.d... xpc_connection_send_message_with_repl... 2 Security securityd_message_with_reply_sync + 70 3 Security securityd_send_sync_and_do + 46 4 Security __SecItemAdd_block_invoke + 166 5 Security SecOSStatusWith + 14 6 Security SecItemAdd + 338 7 ICQ -[KeychainItemWrapper writeToKeychain] 8 ICQ -[MRAuthenticationManager setKeychainV... 9 ICQ -[MRAuthenticationManager setPasscode:] ... Операции не место на главном потоке – выносим в фоновый поток
  12. 12. Анализ стека в крэшлоге Thread 0: 0 libsystem... semaphore_wait_trap + 8 1 AudioToolbox playerasync_Invalidate + 228 2 AVFoundation -[AVPlayer dealloc] + 280 3 AVFoundation -[AVPlayerLayer dealloc] + 184 4 Foundatio... NSKVODeallocate + 88 5 AVFoundation __destroy_helper_block_339 + 24 6 libsystem... _Block_release + 152 7 libdispat... _dispatch_client_callout + 12 8 libdispat... _dispatch_main_queue_callback_4CF Thread 30: 0 libsystem... mach_msg_trap + 8 1 AudioToolbox ASClient_AudioSessionSetActiveW... 2 AudioToolbox AudioSessionSetActiveWithFlags + 112 3 libAVFAud... -[AVAudioSession setActive:withOp... 4 ICQ __48-[SoundManager deactivateAudioSession... Нашего кода нет в главном потоке. Возможно, он ожидает завершения нашей работы в другом потоке
  13. 13. Профилирование
  14. 14. https://www.raywenderlich.com/97886/instruments-tutorial-with-swift-getting-started
  15. 15. Что здесь происходит? Процессор ничем не занят, но UI висит Загрузка процессора при блокировках
  16. 16. Запись ждущих потоков
  17. 17. Запись ждущих потоков
  18. 18. Поток ждет на семафоре
  19. 19. Нельзя просто так измерить время операции 1 2 3 3 4 5
  20. 20. Результаты на проекте ICQ
  21. 21. Пример блокировки. CoreData Thread 0: 0 libsqlite3.dylib 0x0000000194391454 sqlite3_value_text + 13068 ... 4 libsqlite3.dylib 0x000000019437f368 sqlite3_step + 524 5 CoreData 0x00000001824e47e8 _execute + 120 6 CoreData 0x00000001824e4420 -[NSSQLiteConnection execute] + 2108 ... 15 CoreData 0x00000001824eac5c -[NSPersistentStoreCoordinator executeRequest... 16 CoreData 0x00000001824e96e0 -[NSManagedObjectContext executeFetchRequest... 17 ICQ 0x00000001001a37f4 __69+[OBJCIM_Profile nicknameForConferenceParticipantWithUid:profilePid:]_block_invoke463 (objcimprofile.mm:1011)
  22. 22. Пример блокировки. CoreData Thread 0: 0 libsystem_kernel... 0x01968e4e48 semaphore_wait_trap + 8 1 libdispatch.dylib 0x01967c2ee0 _dispatch_barrier_sync_f_slow + 516 2 CoreData 0x0184f0ccb4 _perform + 176 3 CoreData 0x0184e5e9b4 -[NSPersistentStoreCoo...newValuesFor... 4 CoreData 0x0184e5dfd4 _PFFaultHandlerLookupRow + 348 5 CoreData 0x0184e5da48 _PF_FulfillDeferredFault + 248 6 CoreData 0x0184e5d874 _sharedIMPL_pvfk_core + 80 7 ICQ 0x01002e6ad4 __66+[MCChatConversationPresenter...
  23. 23. Пример блокировки. UserDefaults synchronize Thread 0: 0 libsystem_kernel.dylib 0x00000001968e4e48 semaphore_wait_trap + 8 1 CoreFoundation 0x0000000185120518 -[CFPrefsPlistSource synchronize] + 32 2 CoreFoundation 0x0000000185141398 -[CFPrefsSearchListSource alreadylock... 3 CoreFoundation 0x000000018514041c +[CFPrefsSearchListSource withSearchListForIde... 4 CoreFoundation 0x00000001851d0520 _CFPreferencesAppSynchronizeWithContainer + 8 5 Foundation 0x0000000185fd8db0 -[NSUserDefaults(NSUserDefaults) synchronize] + 32
  24. 24. Пример блокировки. Масштабирование картинки Thread 0: 0 CoreGraphics 0x00000001859ad04c argb32_sample_argb32 + 788 1 CoreGraphics 0x00000001859a2a18 RGBA32_image + 1896 2 libRIP.A.dylib 0x0000000185d3b1fc ripl_Mark + 28 3 libRIP.A.dylib 0x0000000185d3a5f4 RIPLayerBltImage + 924 4 libRIP.A.dylib 0x0000000185d3a088 ripc_RenderImage + 256 5 libRIP.A.dylib 0x0000000185d38b58 ripc_DrawImage + 684 6 CoreGraphics 0x000000018597ff5c CGContextDrawImage + 404 7 ICQ 0x00000001005854a4 -[UIImage(Crop) scaleImage:andCropCenter:] 8 ICQ 0x000000010031ec44 -[OBJCWIM_Profile uploadAvatar:forUid:livechat: completion:] (OBJCWIM_Profile.mm:1980)
  25. 25. Пример блокировки. Работа с KeyChain Thread 0: 0 libsystem_kernel.dylib 0x25c6cc74 semaphore_wait_trap + 8 1 libxpc.dylib 0x25d4394b xpc_connection_send_message_with_reply_sync + 176 2 Security 0x2637b1df securityd_message_with_reply_sync + 120 3 Security 0x2637b399 securityd_send_sync_and_do + 46 ... 7 Security 0x2638a109 SecItemAuthDoQuery + 370 8 Security 0x2638ac8b __SecItemDelete_block_invoke + 40 9 Security 0x263891f9 SecOSStatusWith + 14 10 Security 0x2638ac1d SecItemDelete + 362 11 ICQ 0x0093ce8b +[Flurry startupNetworkAndSendSession] + 240 12 ICQ 0x0093ccf5 +[Flurry startSession:] + 1442
  26. 26. Подводные камни
  27. 27. При сворачивании приложения оно “засыпает” До сворачивания После активацииРабота в фоне ● В фоне легко попасть в промежуток, когда приложение выгружено ● При сворачивании и разворачивании выполняются тяжелые системные операции
  28. 28. Системные модули. Клавиатура 6 CoreFoundation __93-[CFPrefsSearchListSource handleReply:toRequestNewDataMessage... 7 libxpc.dylib xpc_array_apply 8 CoreFoundation -[CFPrefsSearchListSource handleReply:toRequestNewDataMessage:onC... 9 CoreFoundation __66-[CFPrefsSearchListSource generationCountFromListOfSources:co... 10 CoreFoundation _CFPrefsWithDaemonConnection 11 CoreFoundation __66-[CFPrefsSearchListSource generationCountFromListOfSources:c... 12 CoreFoundation CFPREFERENCES_IS_WAITING_FOR_CFPREFSD 13 CoreFoundation -[CFPrefsSearchListSource generationCountFromListOfSources:count:] ... 20 CoreFoundation +[CFPrefsSearchListSource withSearchListForIdentifier:container:... 21 CoreFoundation _CFPreferencesCopyAppValueWithContainerAndConfiguration 22 TextInput -[TIPreferencesController valueForKey:] 23 TextInput -[TIPreferencesController boolForKey:] 24 UIKit -[UIKeyboardImpl initWithFrame:]
  29. 29. Системные модули. Клавиатура 5 dyld ImageLoaderMegaDylib::dlopenFromCache(ImageLoader::LinkContext const&... 6 dyld dyld::dlopenFromCache(char const*, int, void**) 7 dyld dlopen 8 libdyld.dylib dlopen 9 TextInput -[TIKeyboardLayoutFactory init] 10 TextInput __48+[TIKeyboardLayoutFactory sharedKeyboardFactory]_block_invoke 11 libdispatch.dylib _dispatch_client_callout 12 libdispatch.dylib dispatch_once_f$VARIANT$mp 13 TextInput +[TIKeyboardLayoutFactory sharedKeyboardFactory] 14 UIKit UIKeyboardGetKBStarName
  30. 30. Системные модули. Клавиатура 0 libsystem_kernel.dylib semaphore_wait_trap 1 libdispatch.dylib _dispatch_semaphore_wait_slow 2 libxpc.dylib xpc_connection_send_message_with_reply_sync 3 libsystem_config… libSC_send_message_with_reply_sync 4 libsystem_config… nwi_state_copy 5 AssistantServices -[AFNetworkAvailability _updateState] 6 AssistantServices __36-[AFNetworkAvailability isAvailable]_block_invoke 7 libdispatch.dylib _dispatch_client_callout 8 libdispatch.dylib _dispatch_barrier_sync_f_invoke 9 AssistantServices -[AFNetworkAvailability isAvailable] 10 AssistantServices -[AFDictationConnection dictationIsAvailableForLanguage:] 11 UIKit +[UIDictationController dictationIsFunctional] 12 UIKit -[UIKeyboardLayoutStar stateForDictationKey:]
  31. 31. Системные модули. Клавиатура 0 libsystem_kernel.dylib __open 1 Foundation _NSReadBytesFromFileWithExtendedAttributes 2 Foundation -[NSString initWithContentsOfFile:encoding:error:] 3 Foundation +[NSString stringWithContentsOfFile:encoding:error:] 4 AppSupport -[CPBitmapStore version] 5 UIKit -[UIKeyboardCache init] 6 UIKit +[UIKeyboardCache sharedInstance]
  32. 32. Системные модули. Камера 0 libsystem_kernel.dylib semaphore_wait_trap 1 libdispatch.dylib _dispatch_semaphore_wait_slow 2 libxpc.dylib xpc_connection_send_message_with_reply_sync 3 CoreFoundation __66-[CFPrefsSearchListSource generationCount... 4 CoreFoundation _CFPrefsWithDaemonConnection 5 CoreFoundation __66-[CFPrefsSearchListSource generationCount... 6 CoreFoundation CFPREFERENCES_IS_WAITING_FOR_CFPREFSD ... 16 CoreFoundation _CFPreferencesGetAppBooleanValueWithContainer 17 CameraKit -[CMKCameraView initWithFrame:spec:] 18 PhotoLibrary -[PLImagePickerCameraView initWithFrame:spec:] 19 PhotoLibrary -[PLUICameraViewController loadView]
  33. 33. Системные модули. Камера 0 libsystem_kernel.dylib __getdirentries64 1 libsystem_c.dylib _readdir_unlocked 2 libsystem_c.dylib readdir 3 CoreFoundation _CFIterateDirectory ... 12 Foundation -[NSBundle localizedStringForKey:value:table:] 13 CameraKit CMKLocalizedFrameworkString 14 CameraKit -[CMKModeDial _titleForMode:] 15 CameraKit -[CMKModeDial reloadData] ... 20 PhotoLibrary -[PLUICameraViewController loadView]
  34. 34. Системные модули. Камера 13 ImageIO CGImageSourceCreateImageAtIndex 14 UIKit ImageRefAtPath 15 UIKit GetImageAtPath 16 UIKit -[_UIPathLazyImageAsset imageWithTraitCollection:] 17 UIKit +[UIImage imageNamed:inBundle:compatibleWithTraitCollection:] 18 UIKit +[UIImage(UIImagePrivate) imageNamed:inBundle:] 19 CameraKit -[CMKFlipButton _flipImage] 20 CameraKit -[CMKFlipButton _commonCMKFlipButtonInitialization] 21 CameraKit -[CMKFlipButton initWithFrame:] 22 UIKit +[UIButton buttonWithType:] 23 CameraKit -[CMKCameraView _createFlipButtonIfNecessary] 24 CameraKit -[CMKCameraView initWithFrame:spec:]
  35. 35. Системные модули. Камера 7 libxpc.dylib xpc_connection_create 8 libxpc.dylib xpc_connection_create_mach_service 9 AudioToolbox SSClientIPC::ConnectToServer() 10 AudioToolbox SSClientIPC::SSClientIPC() 11 AudioToolbox ___ZN10TSingletonI11SSClientIPCE8InstanceEv_block_invoke 12 0x210f980d 13 0x2110b171 14 AudioToolbox InitializeSystemSoundClient() 15 AudioToolbox AudioServicesAddSystemSoundCompletion 16 CameraKit -[CMKCameraView _registerForSystemSound]
  36. 36. Внешние библиотеки. Facebook SDK Thread 0: 0 libsystem... semaphore_wait_trap + 8 1 Accounts -[ACAccountStore accountTypeWithAccountTypeIdentifier:] + 316 2 ICQ -[FBSDKSystemAccountStoreAdapter accountType] 3 ICQ -[FBSDKSystemAccountStoreAdapter accessTokenString] 4 ICQ -[FBSDKGraphRequestConnection processResultBody:error:metadata:] 5 ICQ __64-[FBSDKGraphRequestConnection completeWithResults:networkErr... 6 CoreFound... __53-[__NSArrayM enumerateObjectsWithOptions:usingBlock:]_block_... 7 CoreFound... -[__NSArrayM enumerateObjectsWithOptions:usingBlock:] + 230 8 ICQ -[FBSDKGraphRequestConnection completeWithResults:networkError:] ... 13 ICQ -[FBSDKURLConnection connectionDidFinishLoading:] 14 Foundati... __65-[NSURLConnectionInternal _withConnectionAndDelegate:onlyAct... 15 Foundati... -[NSURLConnectionInternal _withConnectionAndDelegate:onlyActive...
  37. 37. Внешние библиотеки. AppsFlyer Thread 0: 0 libsystem... semaphore_wait_trap 1 libdispat… _dispatch_group_wait_slow 2 CoreFound... CFPREFERENCES_IS_WAITING_FOR_CFPREFSD 3 CoreFound... -[CFPrefsPlistSource synchronize] 4 CoreFound... -[CFPrefsSearchListSource alreadylocked_requestNewData] 5 CoreFound... __95+[CFPrefsSearchListSource withSearchListForIdent... 6 CoreFound... normalizeQuintuplet 7 CoreFound... +[CFPrefsSearchListSource withSearchListForIdentifie... 9 CoreFound... _CFPreferencesAppSynchronizeWithContainer 10 Foundation -[NSUserDefaults(NSUserDefaults) synchronize] 11 ICQ -[AppsFlyerTracker getCounter:]
  38. 38. Внешние библиотеки. Flurry Thread 0: 0 libsystem... lstat + 8 1 Foundation _NSStandardizePathUsingCache + 2068 2 Foundation -[NSString(NSPathUtilities) _stringByStandardizingPathUsingCache:] + 128 3 Foundation _NSExpandTildeInPath + 104 4 Foundation -[NSString(NSPathUtilities) stringByExpandingTildeInPath] + 108 5 Foundation NSSearchPathForDirectoriesInDomains + 296 6 ICQ +[FlurryUtil filePathDirectory] + 64 7 ICQ -[FlurryGlobalVariableStorage getPersistentFilePath:] + 60 8 ICQ -[FlurryGlobalVariableStorage initPersistentMap] + 40 9 ICQ +[Flurry startSession:] + 640
  39. 39. Workarounds Костыли
  40. 40. Остановка и запуск До сворачивания После активацииРабота в фоне Важно остановиться как можно раньше при сворачивании и стартануть как можно позже при активации Стоп Старт
  41. 41. Пропуск промежутка Стоп Старт UI thread
  42. 42. Swizzled Перехват и пропуск Стоп Старт UI thread
  43. 43. Значение временного лимита 1 секунда 3 секунды Выключен
  44. 44. На позитивной ноте • Мы оперативно понимаем, что вызывает торможение на главном потоке • Это настолько удобно, что хочется так же на остальных потоках https://github.com/maxgordeev/MRWatchdog

×