(Case 940893) Physics.SyncTransforms() performance issue

The Profiler in Unity 2017.2.0b6 shows that manually syncing transforms, once per update, is slower than the Physics system performance in earlier Unity builds, such as 2017.1.1p1.

According to the Physics.autoSyncTransforms information I could find, one reason to introduce this new API in 2017.2 is to provide a way to help to improve performance.

However, my tests show that physics performance is worse in both cases, with autoSyncTransforms enabled and disabled, compared to Unity 2017.1.1p1.

Manually syncing transforms (using Physics.autoSyncTransforms=false and Physics.SyncTransforms()) is about 1ms slower than the physics system in 2017.1.1p1 in my tests.
2017.2.0b6 - CPU time between 2.6ms - 5.0ms by average
2017.1.1p1 - CPU time between 2.3ms - 4.3ms by average

Please see the video attached to the bug-report.
Video

Reproduce

  • Open user attached project
  • Open Assets/Scene.unity
  • Open Window/Profiler
  • Press Play
  • Write down CPU time.

Repeat these steps with Unity 2017.1 and observe 2017.2.0b6 is slower.

Expected
Physics should not be slower than in a previous version.

3 Likes

Excellent bug report and find Peter good work…

1 Like

From what I understand, even with the autoSync off, SyncTransforms() is called before the physics simulation step. So if you’re calling SyncTransform manually once, it’s actually being called twice.
There are other things that would cause the update as you can see here.
And maybe have a dirty bit, making the test more difficult but not less important.

1 Like

I think you’re right. I wasn’t aware it still syncs transforms even after telling the system to not do that. I’ve to check whether performance is OK if I remove that SyncTransforms() call.

Thanks for the info, very helpful!

Excellent find. Have you tested performances in builds (not in-editor), just in case?

I know sometimes certain features generate debug information in-editor and are therefore much slower. For instance, GetComponent() can generate GC allocs in-editor if the component was not found, because it creates a new warning message string.

But I’m not sure if a development build would have the same problem… maybe the only real way to “profile” it would be to make a very physics-heavy scene and then compare framerates?

Yes, the numbers I posted are from a Windows Standalone build. But I’m not sure if that’s really an issue, I need to re-test without calling SyncTransforms().

1 Like

I profiled this case too now, where I do not call SyncTransforms(). CPU time did improve, but it’s still about 0.5ms slower than 2017.1.1p1. Will update the bug-report now, to let QA know about the recent findings.

Unity just exposed physics sync in the API. Since access to the API can only be done in the main thread I wonder if this is why those old physics bits now show up in the profiler, where previously, they were done invisibly in a separate thread.

When you change a Transform in any way, an internal notification message gets sent to components on the same GO and all its children. This could become expensive depending on the components reacting to it. For physics components, often they needed to perform a (relatively) costly update to ensure that physics was synchronised. This update happened synchronous to the Transform change as well. It also meant that if you modified a Transform in multiple steps like setting position, rotation or scale separately then it happens multiple times.

In 2017.2, due to internal requirements of the C# job system, this change notification was removed from the Transform system and replaced with an alternate method that used the C++ job system to retrieve a set of Transforms that have changed. This meant that component owners such as the 2D and 3D physics teams needed to deal with these consequences and make changes that ensured that legacy behaviour was maintained but also that the new behaviour could be used.

Now when a Transform changes, no components are updated because there isn’t a notification from the Transform system at all! This means its faster but it also means that the associated components need to be synchronised to the Transform change(s) but it is deferred until required.

So when is it required? From the physics system POV, a SyncTransform is done prior to updating the simulation during fixed update or a manual physics simulation. This always happens. So, you can change hundreds of Transforms hundreds of times and no components get updated, so if this was a pattern in your project before, you get that back and only the Transforms that have changed get processed, just once, no matter how many times they were changed.

Now to ensure support for previous behviour we need to perform a SyncTransform prior to any physics query to ensure that the query is using the latest changes to physics. Whilst this guarantees previous behaviour, it has a small cost associated with performing it, even when no transforms have changed. This is the AutoSyncTransforms option in 2D and 3D physics. With this off, the only update is prior to the simulation being run but means physics won’t be in sync to any changes you’ve made until the next update. You are free to manually SyncTransforms which is the call that is made when AutoSyncTransforms is on.

When we perform a SyncTransform, we do most of the work off the main-thread so in sum-total, the actual update should be faster as previously everything was on the main thread however, performing an AutoSyncTransform prior to any query is work we didn’t do before. Even when no Transforms have changed, there’s a slight cost which is a pain however the team dealing with Transform dispatch (as it’s known internally) are improving this.

Therefore, the best performance is when AutoSyncTransforms is off i.e. it’s only sync’d prior to updating the simulation. Only call SyncTransforms if absolutely essential and avoid calling it if not needed.

This is the solution we were forced to implement due to the Transform change notification being removed in the Transform system. I hope it provides some useful background on what changed.

9 Likes

It does, thanks for the explanation!

It’s a difference of 50ms according to my tests.
2017.1 requires about 4ms to complete one frame, 2017.2 requires 54ms. According to several other tests I did, it seems every project runs slower in 2017.2 and it seemed to me, according to the Unity project, that the cause are the physics system related changes.

I can’t afford a 10x performance degradation in my project, which is the case if I just open it in 2017.2.
However, if it’s as easy as turning off autoSyncTransforms to restore the performance of 2017.1 or make it even run faster, perhaps that should be the default setting then?

That’ll break existing projects that expect the physics to be updated each time a Transform is changed. In the future, new projects will have this off by default.

The performance is in the hands of the Transform system. The actual performing physics sync is much faster, the checking to see if any Transforms have changed is the overhead. This is the part which is out of the physics teams hands and should improve considerably, especially in the case of no Transforms changed.

Really appreciate the wealth and depth of information.

1 Like

I do wonder if there would be any performance benefit to allowing a mode where you can make particular rigidbodies never update from a transform automatically before simulation, for projects where the developer can guarantee that this will not happen.

Note that the Rigidbody(2D) won’t get updated if the Transform hasn’t changed. Only those Transforms that have changed update Rigidbody(2D) and Collider(2D).

Just dropping in to confirm that even with autoSync off and doing absolutely no manual Syncs, the FixedUpdate.ScriptRunBehaviourFixedUpdate entry in the profiler is about 2x slower than in 2017.1 for a script that does a bunch of Raycasts, CapsuleCasts and Rigidbody.MovePosition/Rotation

(for 3D physics, in 2017.2b8)

1 Like

I’ll speak to Anthony Y (3D physics) because that doesn’t make sense. Those calls haven’t changed and if AutoSync is off, then the sync before the query executes isn’t run so they should be identical performance.

Are you sure it’s related to the actual queries you’re performing?

I’ll admit that I tested this with a pretty huge character controller script that does a whole bunch of stuff. I’ll make a better test later today and upload it here

Thanks. I mentioned it to Anthony. I’ll mention you’re uploading an example.

Okay, my bad. I was wrong

The test I’ve made shows no performance difference at all. It includes:

  • raycasts
  • capsuleCasts
  • OverlapSpheres
  • computePenetrations
  • direct rigidbody moves
  • interpolated kinematic rigidbody moves (MovePosition, MoveRotation).

Actually 2017.2 even seems to perform slightly better for the PhysicsFixedUpdate task.

However, I still see a performance difference for my character controller script. I’ll have to keep searching for the culprit. With all those physics queries I just cleared of suspicion, it may not be physics-related at all

I’ve attached the test project anyway for those who might be interested. Just open the testScene, press play, and look at the profiler.

PS: This is to be expected, but the performance difference in 2017.2 between having AutoSync on/off is absolutely immense. 11ms versus 650ms per FixedUpdate in my case. People will really have to be well educated on this feature, otherwise they may start wondering why their 60fps game suddenly runs at 1fps. At this point, I’m wondering if activating AutoSync should be an option at all… maybe just forcing people to understand syncing and having it constantly off would be a better option? I understand that the notion of rigidbody syncing might not be understood by a large part of the community, but the performance cost is just waaaaay too huge for this to be a viable thing to do

3192371–243866–PhysicsTests.zip (463 KB)

1 Like

I dont understand why there is such a big performance regression since the engine has always been running in ‘AutoSync on’ mode?