Have you used the Unity Profiler to make sure the rendering is your issue?
I'm a Unity Programmer so sadly that falls outside of my scope however I do have some tips for coding that may help.
1. Don't use GetComponent / GetObjectOfType in the update loop (unless you have to). These are resource intensive functions in Unity. Ideally you would cache the data in a variable and have it be assigned during the Start/Awake functions.
2. If you are instantiating a lot of prefabs in the update loop, you may wish to consider using Object Pooling instead. This recycles unused GO's instead of Destroying them. Brackeys has a video explaining the benefits of pooling on youtube if you wish to know more. Additionally I do have a Unity Package available on my itch page that you can download (It's free) that will handle the logistics of pooling for you, I do believe it has an example scene included if you don't know how to use it. (if not you can always reach out to me through this comment.)
Other than that all I can really recommend, without having a look at your code, is that you try to move logic that doesn't need to be in the update loop out. For example:
There is a button that handles the activation of a ui panel by setting a bool in your code. The ui panel, or its manager, checks this bool every frame (in the update) to see whether it should turn on or not.
Obviously for this example you would just have the button directly turn this panel on when you click it instead, however what I'm trying to suggest is that the ui panel does not need to know every frame whether it should turn on or not. You simply turn it on the moment the button is pressed. What this means is that instead of every frame some logic runs to check whether the ui should be on, this same logic runs only on one frame to turn it on.
I hope your optimization adventure goes well and that you can take back your frames. Happy hunting!