Welcome, Guest. Please login or register.

Login with username, password and session length

 
Advanced search

1411471 Posts in 69369 Topics- by 58423 Members - Latest Member: antkind

April 23, 2024, 09:37:31 AM

Need hosting? Check out Digital Ocean
(more details in this thread)
TIGSource ForumsDeveloperTechnical (Moderator: ThemsAllTook)Optimization : operators of Vector3 and other structs
Pages: [1]
Print
Author Topic: Optimization : operators of Vector3 and other structs  (Read 1225 times)
btata
Level 0
**


View Profile
« on: August 21, 2017, 03:00:08 AM »

Hi,
In this link, you can find a way to optimize operators (multiplication, addition, etc) of some heavily used Unity's structures (Vector3, Vector2, etc).
https://feedback.unity3d.com/suggestions/vector3-and-other-structs-optimization-of-operators
You can also vote for the optimization, following the same link, so it can be included into the Unity engine.
Logged

bateleur
Level 10
*****



View Profile
« Reply #1 on: August 22, 2017, 06:35:45 AM »

Hang on a minute, though... Unity's Vector3 is a value type, so won't it cause problems if you change the implementation that way? For example...

Code:
function multiplySomeVectors(Vector3 v, SomeClass someObject) {
 someObject.setVectorValue(v * 2.0f);
 someObject.setOtherVectorValue(v * 3.0f);
}

If the implementation of * does not create a new Vector3, won't the call to setOtherVectorValue get passed the wrong value? (Specifically 6x rather than 3x the input value passed to multiplySomeVectors.)
Logged

Cheesegrater
Level 1
*



View Profile
« Reply #2 on: August 22, 2017, 07:54:08 AM »

It still creates a new Vector3, it just does so without calling the constructor.
Logged
BorisTheBrave
Level 10
*****


View Profile WWW
« Reply #3 on: August 22, 2017, 12:58:40 PM »

I'm guessing this is because Unity uses some ages old version of .NET? Or maybe because the JIT wasn't warmed before benchmarking. I cannot see this optimization making much difference in the modern world.

Reminds me (and I bet bateleur, too), of the bad old days of Flash development.
Logged
qMopey
Level 6
*


View Profile WWW
« Reply #4 on: August 22, 2017, 01:16:50 PM »

Reminds me (and I bet bateleur, too), of the bad old days of Flash development.

Oh no was it really bad? Sad

I'm a little too young to have gotten into flash dev, and by the time I finally learned to write good code Apple had already killed Flash. What was it like? I've sort of romanticized the idea of writing in flash Concerned Every now and then a visit is paid to the old Flixel repo on github just to look around and wonder what it as like.
Logged
bateleur
Level 10
*****



View Profile
« Reply #5 on: August 23, 2017, 01:44:33 AM »

Reminds me (and I bet bateleur, too), of the bad old days of Flash development.

<clutches whiskey bottle> <mutters darkly>
Logged

oahda
Level 10
*****



View Profile
« Reply #6 on: August 23, 2017, 04:11:40 AM »

Latest Unity updated C# and .NET versions so that's luckily not true anymore.
Logged

btata
Level 0
**


View Profile
« Reply #7 on: August 23, 2017, 07:20:21 AM »

I'm guessing this is because Unity uses some ages old version of .NET?

Latest Unity updated C# and .NET versions so that's luckily not true anymore.

I tried with Unity 2017.1.0f3, while activating its support for .Net 4.6 and the optimization still works.
Logged

btata
Level 0
**


View Profile
« Reply #8 on: August 23, 2017, 07:37:10 AM »

Or maybe because the JIT wasn't warmed before benchmarking.

Warming up doesn't remove the benefits of this optimization. The reason why it works is explained in this post
https://forum.unity3d.com/threads/vector3-and-other-structs-optimization-of-operators.477338/#post-3112752
To keep it simple, the optimized version avoids calling the constructor. Such a call is not that expensive, but it is relatively expansive compared to the remaining instructions of such operators.

I cannot see this optimization making much difference in the modern world.

There is at least one case where it makes a difference, and it is the case through which I discovered this optimization: real time mesh generation. In such cases, and specially for big meshes (in my case it was the mesh of a racing game track) you end up easily doing hundreds of thousands of Vector operations. This optimization made my mesh generation possible in real time.

But I agree that for the general case, this will not make a huge difference for a big part of Unity users. But the optimization is soooo easy to implement and test, I find it a shame that no one at Unity spend a couple of hours to do so.
Logged

oahda
Level 10
*****



View Profile
« Reply #9 on: August 23, 2017, 09:32:01 AM »

Sorry, was replying to Boris and not OP in case that wasn't clear.
Logged

bateleur
Level 10
*****



View Profile
« Reply #10 on: August 24, 2017, 01:39:06 AM »

To keep it simple, the optimized version avoids calling the constructor.

So, as per my original comment above, is this not in fact a change of functionality? I assume it works in your specific use case, but in general multiplying a Vector3 by a float might need a new instance in order to avoid side effects. Or am I hopelessly confused about something?
Logged

Cheesegrater
Level 1
*



View Profile
« Reply #11 on: August 24, 2017, 05:52:46 AM »

So, as per my original comment above, is this not in fact a change of functionality? I assume it works in your specific use case, but in general multiplying a Vector3 by a float might need a new instance in order to avoid side effects. Or am I hopelessly confused about something?

Yes, you seem to be confused. The optimized code is creating a new instance without calling the constructor. The two things are not synonymous.

As long as the Vector3 doesn't end up with any uninitialized memory this is harmless and saves the overhead of a function call.
Logged
BorisTheBrave
Level 10
*****


View Profile WWW
« Reply #12 on: August 24, 2017, 12:32:53 PM »

Yes, I can see that one calls the constructor and the other relies on default construction. That gives different IL, as the OP shows. That is why I specified *JIT*, not compiler. For value types, such constructors are trivially inlinable, I'd be disappointed if the JIT couldn't reduce these two cases to the same machine instructions, even though they have different IL.

I haven't tested for myself, but I note the original test doesn't give the JIT a chance to warm up.
Logged
btata
Level 0
**


View Profile
« Reply #13 on: August 28, 2017, 03:52:39 AM »

I haven't tested for myself, but I note the original test doesn't give the JIT a chance to warm up.

Indeed, the posted test doesn't give the JIT a chance to warm up, but it was the simplest test to understand for people. I can assure you that, before posting this simple test, I tested the optimization in a game session that was started since minutes, and had already run the optimized code multiple times, and the benefits were still there.
Logged

bateleur
Level 10
*****



View Profile
« Reply #14 on: August 29, 2017, 12:46:37 AM »

Interesting result!  Hand Thumbs Up LeftTiger

Being a natural skeptic I had to test this for myself. And indeed btata is correct. On my machine there is a performance saving of about 30%.

Here is the code I used, in case anyone spots a flaw in my test setup or wants to experiment:

Code:
using UnityEngine;
using System.Diagnostics;

public class TestJIT : MonoBehaviour {

 // This is a test to verify an inefficiency in Unity's built in Vector multiplication
 // Original observation and optimised code by btata on the TIGSource forums

 // To use this test, create an empty scene, add an empty game object, add this script to it, then run.
 // The results appear in the Console panel.

 // These three public variables can be adjusted in the inspector with the code running to test the behaviour
 // of the different implementations. If you see very low timing numbers, increase the number of cycles. To test
 // the efficiency of avoiding a constructor call, set useConstructor to false. To test Unity's built-in
 // implementation, set noCall to true.

 public int cyclesPerFrame = 1000000;
 public bool useConstructor = true;
 public bool noCall = false;

 public void Update() {
  if (noCall) {
   // The Unity built in implementation always uses a constructor
   useConstructor = true;
  }
  Stopwatch sw = Stopwatch.StartNew();
  Vector3 v = new Vector3(-1,0.1f,100);
  float s0 = 0.5f;
  float s1 = 2.0f;
  if (useConstructor) {
   for (int i=0; i<cyclesPerFrame; i++) {
    if (noCall) {
     v *= s0;
     v *= s1;
    } else {
     v = multiplyWithConstructor(v,s0);
     v = multiplyWithConstructor(v,s1);
    }
   }
  } else {
   for (int i=0; i<cyclesPerFrame; i++) {
    v = multiplyWithoutConstructor(v,s0);
    v = multiplyWithoutConstructor(v,s1);
   }
  }
  UnityEngine.Debug.Log("Time taken: " + sw.ElapsedMilliseconds + " ms");
 }

 private Vector3 multiplyWithConstructor(Vector3 a,float d) {
  return new Vector3(a.x * d, a.y * d, a.z * d);
 }

 private Vector3 multiplyWithoutConstructor(Vector3 a,float d) {
  Vector3 result;
  result.x = a.x * d;
  result.y = a.y * d;
  result.z = a.z * d;
  return result;
 }

}
Logged

Pages: [1]
Print
Jump to:  

Theme orange-lt created by panic