Potential performance bottleneck when using reflection. #25
Replies: 7 comments 9 replies
-
Hey @RabugenTom, The performance of case path reflection is a known issue, and it's definitely a bummer. We're not sure there is anything we can do to speed it up. Ideally Swift would provide first class support for this (like they do for key paths), which would make it much much faster. We have found that reflection case paths work just fine as long as actions aren't being sent at a rapid pace. We're working on a large application right now and the reflection hasn't been an issue yet. However, if you do have some actions that are being sent in great volume, then you may need to write your case paths by hand rather than relying on the reflection. There's a discussion in this issue on TCA discussing this very topic: pointfreeco/swift-composable-architecture#327 Let us know if that helps at all. Also, I do not believe your performance problems are coming from usage of If your state is really large then I suppose there's a chance that printing the state is taxing your CPU and causing everything to slow down. However, none of that would show up as work on the main thread in instruments. Further, I don't think the code in EnumReflection.swift is executed at all for the |
Beta Was this translation helpful? Give feedback.
-
Thanks Brandon, I'll look at the hand-crafted case paths. I wasn't aware of them. I'm already using Sourcery to generate a bunch of things in my app, so I guess can write a template to generate case paths automatically. I'll report if it solves the issue when restoring default When I'm talking high rate, I'm considering a some kind of evolved macOS word processor using SwiftUI with two sidebars and many features (like between 5 and 10 "iOS screens" visible at the same time). There is not much I can do to lower actions traffic below the point I'm at, but as I said, the apps runs fine as soon as I'm able to mute my types' Also, I'm not using
Upon closer inspection, it seems that the issue may not arise from |
Beta Was this translation helpful? Give feedback.
-
I've written some Sourcery template to generate all my case paths. I confirm that using these explicit definitions resolves my performance issues. I'm attaching the template here in any case someone wants to make use of it. You can also include it in
|
Beta Was this translation helpful? Give feedback.
-
Hey @RabugenTom, thanks for the info! That is very interesting. We'd love to figure out how to make the reflection API faster. If you figure anything out please let us know. I'm pretty surprised that stack trace is so deep for you. Wonder what Thanks! |
Beta Was this translation helpful? Give feedback.
-
Hey @mbrandonw, I've checked again and the issue is not where I thought it was (twice…). I've implemented some custom debug description in one of my data type and put a breakpoint to see where it was called. I should have done this from the start instead of making you loose your time. The issue arises from L70, in About my stack trace being deep, this is not so surprising given the architecture of my app (and this doesn't take so long to dive into it when we are not in the special cases presenting the issue). It's a macOS app which allows to write songs with chords and tablatures with an enhanced text editor. What you see in the stack trace up to |
Beta Was this translation helpful? Give feedback.
-
@RabugenTom Not sure if you ever revisited this, but I'm going to move to a discussion. Maybe more folks will have ideas they can share there! |
Beta Was this translation helpful? Give feedback.
-
@RabugenTom we’ve recently merged a bunch of performance improvements to this library. I’d be curious if you see any changes in your instruments. |
Beta Was this translation helpful? Give feedback.
-
I'm reporting an issue I had with TCA. My app was lagging a lot and instruments showed that a bunch of the time was spent on the main thread computing debug description strings. This is caused by
Mirror(reflecting:)
which is used when trying to reassign local cases (EnumReflection.swift#L61).I have huge data structs, with many containers and
NSAttributedString
, and generating the debug description doesn't come for free. The app is quite complex, but in the current state, ~80% of its work on the main thread is spent extractingCasePath
's. I temporarily improved the situation by carped-bombing my data structs with a some protocol extendingCustomReflectable
and providing a very small default implementation, but this is far from optimal.I think that this kind of call is not expected to happen on the main thread, furthermore at a high frequency rate like it happens in a reducer.
Maybe the result can be cached, or another approach to reflection can be used, with some kind of (optional) protocol or prescription over
Action
enums?Beta Was this translation helpful? Give feedback.
All reactions