3

I have an app which I am trying to migrate to a new project. There is a heavy operation which I am handling in main thread asynchronously. In my older project, it takes just a second to complete this task but in my new project, it takes 6-7 seconds for the same task.

I observed the CPU usage and it looks like the new app is using less CPU and getting very few threads while the old one gets lots of threads for the same task. PS: I am using the same device.

What could cause this? Any ideas or suggestions to find out?
Thanks.

ysnzlcn
  • 558
  • 6
  • 18
  • Would you narrow to the code doing this and show for analyses? – Asperi Apr 07 '20 at 12:56
  • Hi Asperi, i am using dlib and opencv for face and landmark detection. Since they are too big i cant share any example project.In my old project it also used to complete task in 6-7 sec, but after ios 13 it decreased to 1 sec. But i also run the new project in same device with iOS 13. Maybe with time iOS marks and gives more thread to projects which are using high CPU? – ysnzlcn Apr 07 '20 at 13:19
  • Hard to tell w/o any code. However, to me it sounds as if your old app was actually doing the task on a background thread/queue, because you tell that it would get allotted more threads while using more cpu. I would check the threading setup. Why are you using the main thread in the first place? you can schedule OpenCV tasks well to the background. – nine stones Apr 08 '20 at 01:22
  • Hi @ninestones, i had to use the main thread because the user is waiting for detection and background queue takes almost 2 mins to complete the task. The code is the same in both projects line by line. I think the issue lies in project settings. Something like architectures or bitcode related stuff. But i don't wanna mess with them without making sure of changes. – ysnzlcn Apr 08 '20 at 08:19

2 Answers2

3

Finally, I found the problem. It was caused by Optimization Level setting in Xcode Build Settings. When a new project created, default Debug optimization level is none, and Release optimization level is Fastest, Smallest [-Os] So when I changed Debug to Fastest, Smallest [-Os] my task completion time dropped to 1 sec.

From Apple:

The Xcode compiler supports optimization options that let you choose whether you prefer a smaller binary size, faster code, or faster build times. For new projects, Xcode automatically disables optimizations for the debug build configuration and selects the Fastest, Smallest option for the release build configuration. Code optimizations of any kind result in slower build times because of the extra work involved in the optimization process. If your code is changing, as it does during the development cycle, you do not want optimizations enabled. As you near the end of your development cycle, though, the release build configuration can give you an indication of the size of your finished product, so the Fastest, Smallest option is appropriate.

If you want to read more about optimization levels and performance: Tuning for Performance and Responsiveness

Side note: Changing the optimization level to fastest, Smallest [-0s] in debug mode might affect the debugger breakpoints and it will behave abruptly.

Cheers.

ysnzlcn
  • 558
  • 6
  • 18
  • 1
    Note that changing the optimization level to fastest, Smallest [-0s] in debug mode might affect the debugger breakpoints and it will behave abruptly. That's why it is enabled by default for release as the debugger is not required in release mode. – abhinavroy23 Apr 13 '20 at 09:16
  • Thank you for reminder @abhinavroy23, i will add this to my answer so everyone can see this before changing. – ysnzlcn Apr 13 '20 at 09:32
1

This probably isn't really a response to your question, you answered it yourself quite nicely, but nonetheless I feel like it's needed.

I'd like to stress out how you should NOT make long running operation on the main thread. For no reason. Actually, if you want the screen to refresh 60 times per second (which should always be your goal) it means that every block of code you submit to the main thread must last less than 0.016 seconds (1/60) to avoid losing some frames. If, in the meantime, you also need to make the main thread do some complex animation and other stuff, well probably you need to go far behind the 0.016 seconds point.

If you block the main thread for much more than that (like 1 second in this case) than the users will experience a stuck interface, they can't scroll a scrollView or navigate the app. They may as well close your app entirely since they may feel like it's stuck.

In your case, for example, you may want to add some nice loading animation, like the ActivityIndicator or some nicer animation, to express you're actually working at that moment and you didn't freeze. That's really expected by users nowadays. You may (or may not, it's up to you) also wanna add a cancel button, if the user wants to cancel the long running operation and do something else with your app.

To avoid what you say that causes the loss of performances (the task is slowed up to 7-8 seconds) you may want to use a serialQueue with a high quality of service. Probably userInitiated is what you want.

This way you still have those task be prioritized by the OS, but you won't block the main thread in meantime, allowing you to add that loading animation for example.

If that's still too low of a performance, you could think of splitting the task in subtasks and having them performed in parallel by using DispatchQueue.concurrentPerform(iterations:execute:) (but I don't know if that's doable in your case).

I hope this helps you. Cheers

Enricoza
  • 1,101
  • 6
  • 18
  • Hi @Enricoza, thank you for all suggestions. Of course, I let the user know that the app is processing data by showing the activity indicator while the task is running. Since the purpose of the app depends on these tasks, the user has to wait for it. Also, this is why i need the main thread to execute because the user's purpose of downloading this app is that automation task. Anyways, again thanks a lot. This comment might be very helpful for other people with thread issues. – ysnzlcn Apr 11 '20 at 22:16
  • 1
    Well @ysnzlcn that's just because the activity indicator actually animates off of the main thread (idk how). If you would've opted for a custom (maybe nicer) animation you'd end up with a stuck animation during that time. That, plus the fact that the app may in the future need to be responsive in that time (cancel button), plus the fact that apple says that blocking the main thread for too long may lead to crashes (see DispatchQueue.main documentation page), should make you consider switching to bg thread if you have some spare time. Anyway thanks for the discussion, and happy Easter. – Enricoza Apr 11 '20 at 22:39
  • Hey man, i don't use anything fancy, just good old UIActivityIndicator on UIWindow. Also, i believe i set up all my thread usage wisely, according the app dependencies. Like i said all purpose of this app is processing these tasks and the main thread is best place for performance. Think it like a game app, there are some things have to be done in main thread. Anyways thanks for all the reminders and happy Easter to you, too. – ysnzlcn Apr 11 '20 at 22:52