Search Unity

Question When to use Burst

Discussion in 'Burst' started by DevDunk, Nov 8, 2022.

  1. DevDunk

    DevDunk

    Joined:
    Feb 13, 2020
    Posts:
    5,060
    I find it hard to find when to use Burst in my non ECS projects.
    It can be very fast, so should I burst all (complex) calculations when possible?


    An answer to when you should start looking into burst would be very helpful. I know jobs can add additional overhead, but can burst itself be used without this?
     
  2. AcidArrow

    AcidArrow

    Joined:
    May 20, 2010
    Posts:
    11,794
    You should jobify complex calculations, then use Burst.
     
  3. DevDunk

    DevDunk

    Joined:
    Feb 13, 2020
    Posts:
    5,060
    But not without jobs?
     
  4. AcidArrow

    AcidArrow

    Joined:
    May 20, 2010
    Posts:
    11,794
    AFAIK, no.
     
    DevDunk likes this.
  5. Antypodish

    Antypodish

    Joined:
    Apr 29, 2014
    Posts:
    10,779
    You can use burst just fine without jobs.
    If you write burstable code, then there is no reason to not burst it.

    It may be often easier to write burstable code, than jobifying. You can burst small, to complex methods.

    If using burst in a smart way, it can use SIMD.
    For example, if you got only handfull of iteration loops, there is no point of using multithreaded jobs. For unexperienced, jobs can cost more than they gain.

    There are many cases, when mainthreaded jobs are faster, than trying multhread them.

    But using burst, you can use whenever it is possible.

    Typical use case, where burst can benefit alone without jobs, are simple mobile games.
    Such application will become less energy hungry. Think about batteries. Some algorithms with enabled burst, can gain up to even 10x performance.
     
    Last edited: Nov 8, 2022
  6. AcidArrow

    AcidArrow

    Joined:
    May 20, 2010
    Posts:
    11,794
    What's an example of burstable code that doesn't use the Unity Jobs System?
     
  7. AcidArrow

    AcidArrow

    Joined:
    May 20, 2010
    Posts:
    11,794
    Should I file a bug report for Unity's docs?
    upload_2022-11-9_0-34-36.png
     
  8. Antypodish

    Antypodish

    Joined:
    Apr 29, 2014
    Posts:
    10,779
    Burstable (burst compatible) methods.

    The description you noted from Unity, it describes the compatibility with jobs. It is not a bug. It says "you can" use with jobs. Not that "you must".

    One of examples is, if you main thread jobs or equovalent. That may be the case, when iterating let's say up to 100s or 1000s times simple methods in some let's say for loop. In some loops, when bursted, such bursted methods execution maybe be faster, than trying scheduling multhreaded jobs. And in such case when main threading, you font need run job at all. But it is nice to have, to keep code organised. And maybe preparing for future jobification. Also, you can operate on entities as well.

    For loop is just one of many use cases. But can be any other methods not looped too, as long it is burst compatible.
     
  9. AcidArrow

    AcidArrow

    Joined:
    May 20, 2010
    Posts:
    11,794
    I would appreciate a link to actual code or a talk. The only thing I can find is this:

    https://docs.unity3d.com/Packages/c...dvancedUsages.html#performance-considerations

    which seems pretty limited and is apparently a newer feature (I don't find mentions of this in earlier versions of the manual) and also seems to suggest, you should go ahead and use a job anyway?

    Other than that, every burst code example I see, starts by turning your code into a job.

     
  10. Trindenberg

    Trindenberg

    Joined:
    Dec 3, 2017
    Posts:
    398
    I myself was wondering if and when methods are Bursted, the inspector only tends to show jobs. However, on second look it does show a method I put BurstCompile on, inside a job struct, and that has solved one issue for me.
     
  11. Trindenberg

    Trindenberg

    Joined:
    Dec 3, 2017
    Posts:
    398
    I think it has to be a static method. But in my method working on not many items, got a 2x speedup. Has a setup/call cost of about 50ns (.5 tick), so still great for small methods, just not very simple like a = b + c, but maybe for something simple like: for (int k = 0; k < 20; k++) a[k] = b[k] + c[k];
     
    AcidArrow likes this.
  12. Trindenberg

    Trindenberg

    Joined:
    Dec 3, 2017
    Posts:
    398
    Here's the code that runs twice as fast with Burst, rather than without the method and written in the code.

    Code (CSharp):
    1.         [BurstCompile]
    2.         public static void Execute2(in uint* d, int len)
    3.         {
    4.             while (--len != 0)
    5.                 for (int i = 0; i != len; i++)
    6.                     if (d[i] > d[i + 1]) (d[i], d[i + 1]) = (d[i + 1], d[i]);
    7.         }
    8.  
     
  13. Trindenberg

    Trindenberg

    Joined:
    Dec 3, 2017
    Posts:
    398
    Saying that, d refers to a NativeArray, so regardless of jobs, I'm not sure whether Burst works better (more noticeably) with NativeArrays over a normal array. I think it's more optimised on the Native side than managed but not too sure.
     
  14. Antypodish

    Antypodish

    Joined:
    Apr 29, 2014
    Posts:
    10,779
    Yes, burst is mostly optimised to work with unity.mathematics, (not UnityEngine.Mathematics)
    So using math, instead mathf.
    That means operation on structs and native collections.

    Unity math functions when compiled, can be viewed in in burst inspector. Then you can look for assembly code, and look for methods starting with V letter. I.e. move becomes vmove, that means bursted code is vectorised, and appropriate for SIMD (Single instruction multiple data) . Typically we'll constructed code for SIMD, can run up to 8 instructions in parallel on each core of CPU.

    If I recall correctly matrixes (float4x4) operations has greatest gain, while used with burst. There were some cases some time ago, when using Vector instead float was better in terms of performance. Don't know if this is still true.

    @AcidArrow so unity.math methods are optimised for burst.

    While most samples shows use burst and jobs together, plus often entities, each of these components can exists independently just fine.

    But as long you put [BurstCompile] decoratio above method, which is written in mind to be bursted, it should get compiled in a such way. I suggest trying burst inspector.
     
    Last edited: Nov 9, 2022
  15. DevDunk

    DevDunk

    Joined:
    Feb 13, 2020
    Posts:
    5,060
    I think the tldr is that maybe there should be a tab for this in the documentation
     
    DragonCoder likes this.
  16. DreamingImLatios

    DreamingImLatios

    Joined:
    Jun 3, 2017
    Posts:
    4,271
    Antypodish likes this.
  17. Antypodish

    Antypodish

    Joined:
    Apr 29, 2014
    Posts:
    10,779
    Last edited: Nov 9, 2022
  18. Trindenberg

    Trindenberg

    Joined:
    Dec 3, 2017
    Posts:
    398
    What does this mean on the Unity docs page? Does it mean if it's called in burstable code then it's compiled to a function pointer?


    Code transformation
    Burst uses IL Post Processing to automatically transform the code into a function pointer and call. For more information, see the documentation on Function pointers.

    To disable the direct call transformation, add DisableDirectCall = true to the BurstCompile options. This prevents the Post Processor from running on the code.
     
  19. DreamingImLatios

    DreamingImLatios

    Joined:
    Jun 3, 2017
    Posts:
    4,271
    It means that it replaces the static method call invocation to a function pointer invocation which calls a Burst-compiled version of the function.
     
    j1mmie and Trindenberg like this.
  20. Trindenberg

    Trindenberg

    Joined:
    Dec 3, 2017
    Posts:
    398
    If a parallel job calls a static function is there a problem with that as they are calling the same 1 function? Had some freezeups using static and came to this assumption. Although my goal was to inline, which the inspector seemed to show had happened.
     
  21. DreamingImLatios

    DreamingImLatios

    Joined:
    Jun 3, 2017
    Posts:
    4,271
    If the static function is purely inputs and outputs and stack-local variables and doesn't do anything else, then you can call it from as many threads at once as you want. My guess is something else was causing the freeze-ups.
     
    Antypodish likes this.
  22. unity_CDVKxN7snkcptA

    unity_CDVKxN7snkcptA

    Joined:
    Mar 24, 2020
    Posts:
    13
    Hi, every one. New to Burst system here. Actually I have some similar questions about Burst and Jobs use cases.
    1st, when trying to use them in particular cases, i.e., a mesh converter/generator, considering it need to cover every vertices/triangles, it should be suitable for burst compiler. However, the current code has many managed types involved in, but normally they are not in loops, so is the correct way is, only put the loop part into a Job, but left anything else in the main thread(so as a result the method will be like: main thread part - job part - main thread part - job part - main thread part)? Or I should try package them all into a Job?

    2nd, when I cannot avoid to involved a managed type, was it safe to change everything related into struct, or basically we create a new struct to copy the data, and reform the managed type outside Job part?

    3rd, so I'm dealing with meshes, which means in result, we want tons of mesh could be processed in parallel job too, so it will have "a Job for each meshes in the scene", and in each mesh it will have "calcule jobs which generate mesh details", which from a grand view, a structure of Sub-Job within Job, is this the correct way to do it? If so, considering the first question, is it also safe with the way I've mentioned(if it's the correct way to deal with)?
     
  23. DreamingImLatios

    DreamingImLatios

    Joined:
    Jun 3, 2017
    Posts:
    4,271
    What code? Your code? If so, then make that code not managed. (Profile first to ensure you optimize the right things)

    I don't understand this question.

    You can't schedule a job from a job if that is what you are asking.
     
  24. unity_CDVKxN7snkcptA

    unity_CDVKxN7snkcptA

    Joined:
    Mar 24, 2020
    Posts:
    13
    Yes, I'm talking about my code.
    Let's say, currently I have a 3rd party plugin and some method to use them as a mesh generator already. As a common sense for Burst compiler, I need to turn everything into unmanaged, that's fine. But I wondered is, should I directly change the current code into struct, i.e., there is a class Path, which records some info for mesh build, and some methods like create for initialization, or, make a new struct just for copy these info and ignore those methods(thankfully these info are all primitive types), which means, in the Job part, I won't touch the original managed type at all but only deal with them in pre/post-Job steps(Also one more thing just noted, in the Create method, there is also a tree calculation which I guess it can also made into Jobs?). This is mainly about RAM usage I guess.

    What I learned for Burst and Job is something, which I can pretend it a special calcuation module, and use it to replace calculation part which may has over thousands loops. As a conclusion, if I want to turn my code into Burst and Job, I should keep their current structure(so mostly are in main thread, part of them is in an async Task), but only patch the place which has heavy calculation, right?

    For Job in Job issue, here is a pseudo code:
    Code (CSharp):
    1. foreach (var pathNode in Path){
    2.     var layer = profile.Copy(); //profile is a something global
    3.     layer.FlipNormals(); //This methods has also loop which covered all mesh faces, can also Jobify?
    4.     var newFaces = new ViewerFace(); //result to return
    5.     var faces = newLayer.faces;
    6.     foreach (var face in faces){
    7.         //calcule normals and UVs, etc, result is newFace
    8.         newFaces.Add(newFace);
    9.     }
    10. }
    I have 2 questions:
    1. If job cannot contained jobs, does that mean I should give up the outter loop, but focus on the inner loop, as it's the main part of the calcuation?
    2. The inner loop has an operation which related with outter loop, newFaces.Add(newFace), and I think it might need to be calculated in order, does that mean I can only use IJob, not IJobParallelFor?
     
    Last edited: Nov 28, 2022
  25. DreamingImLatios

    DreamingImLatios

    Joined:
    Jun 3, 2017
    Posts:
    4,271
    If you are trying to make surgical modifications to a codebase full of managed code you did not originally write, then my suggestion is instead of trying to use jobs, use Burst-compiled static methods wherever you can get away with it. And if possible, try to replace any managed arrays or lists with the unmanaged Unity.Collections alternative.

    I don't recommend trying to parallelize things right away, as that usually requires some architectural forethought. And don't forget to profile!
     
  26. cai814037725

    cai814037725

    Joined:
    Apr 25, 2020
    Posts:
    27
    Is mono multithreading and burst faster? There is not much difference in the test
     
  27. DylanF

    DylanF

    Joined:
    Jun 25, 2013
    Posts:
    55
    This is good advice! I got a 40x speedup on a looped vector and matrix math heavy function just from native types, the new math library, and burst (no jobs). After that work I can see better how to do it with jobs, but there isn't much need.

    Definitely don't forget to profile though. For me, there were a couple case where iterating over a native array of structs was needed from managed code - and it was slow. Doing it by reference is one way to solve that:
    Code (CSharp):
    1. var p = Unity.Collections.LowLevel.Unsafe.NativeArrayUnsafeUtility.GetUnsafePtr(mynativearray)
    2. while(looping){
    3.     ref var x = ref Unity.Collections.LowLevel.Unsafe.UnsafeUtility.ArrayElementAsRef<T>(p, i);
     
    MarsLars and DevDunk like this.