[Best practice] Improve your resource performance

Abstract

Recently there has been a feature added to FiveM where slow resources will be shown as a warning for all the players. In this guide we will cover some of the basic steps you can perform to prevent the warning from showing up. The guide is mainly targeted to the developers or server owners who feel courageous :yum:

Is your resource running slow and you have tried everything in this guide? Please make a post here to we can further expand this guide with best practices and pin point the issues your script might have. Any other suggestions are much appreciated!

Background!

perf

Before we can solve the performance issue(s), it would be darn handy to know what, when and how the slow resource warning pops up, for anyone who hasn’t seen the warning message:

The message is comprised of 3 critical data points which you will definitely need in order to debug any issues you are having. The message is formatted as:

[resource] is taking [time] ms (or -[fps] FPS @ 60 Hz)

[resource] is the resource name of which resource is being slow, it directly correlates to the folder name in your resource/ directory of your server.

[time] is the time in milliseconds it took to execute the entire resource. Important to note that this is a continuous measurement, this is not a one time thing (i.e. loading in). The time is an average of 64 ticks, if the average of the 64 samples is greater than 5 ms, the warning message will be shown.

[fps] is the total amount of FPS that is being deducted by running this resource. Keep in mind this is on a 60 FPS basis.

Improving performance

Frequency

One of the most obvious but yet a lot of scripts do this, is the frequency the code is being run at. Often I see script using Citizen.Wait(0), this would mean any code inside that while loop would run every tick, which is bad for performance if there so happen to be expensive logic in there. To me this seems the #1 cause on why the warning message pops up. So here the rule of thumb:

  • Only run code that needs to be ran every tick, for example any native containing “ThisFrame” in their name. (there are obiviously more natives that need to be ran every tick).

  • Think about a reasonable interval for a piece of code to execute at, for example you don’t need to check 60 times per second to check if a player died, instead consider using 1 or 2 times per second to check such a thing.

  • Segregate any code that does not require to be ran every frame. You can use for instance a global variable(s) to influence the code that is being ran every tick (without executing the expensive code).

Example:

Intial code -> Runs at 119ms

Citizen.CreateThread(function()
    while(true) do
        if IsPedDeadOrDying(PlayerPedId()) then
            ShowHudComponentThisFrame(9) -- street name
        end

        if IsPedInAnyVehicle(PlayerPedId(), true) then
            ShowHudComponentThisFrame(6) --vehicle name
        end
      Citizen.Wait(0)
    end
end)

Improved code -> Runs at 7ms

local isDead = false
local inVehicle = false

Citizen.CreateThread(function()
    while(true) do
        if isDead then
            ShowHudComponentThisFrame(9) -- street name
        end

        if inVehicle then
            ShowHudComponentThisFrame(6) --vehicle name
        end
        
        Citizen.Wait(0)
    end
end)

Citizen.CreateThread(function()
    while(true) do
        isDead = IsPedDeadOrDying(PlayerPedId())
        inVehicle = IsPedInAnyVehicle(PlayerPedId(), true)
        Citizen.Wait(500)
    end
end)
the examples are run at 100.000x faster than normal

Natives

Natives are at the core of any script, but do keep in mind that natives are more expensive to call than pure LUA code. Especially when you have a hot path with a lot of natives to be ran, this will acrude the execution time of the natives and increase frame time by quite a bit. So here are the rules of thumb:

  • Think twice before putting (multiple) natives in some hot code path, because they will be slow :mascot:, use alternatives if possible.

  • Cache the result of a native instead of requesting them ad-hoc in a hot code path. A common indicator of wether you should cache something is when you use the same native two or more times in the same scope.

Example

Intial code -> Runs at 346ms

Citizen.CreateThread(function()
  while true do
    local armor = GetPedArmour(PlayerPedId())

    armor = armor + 1
    if armor > 100 then
    armor = 0
    end

    SetPedArmour(PlayerPedId(), armor)

    Citizen.Wait(0)
  end
end)

A slightly better solution -> Runs at 255ms

Citizen.CreateThread(function()
  local armor = GetPedArmour(PlayerPedId())

  while true do

    armor = armor + 1
    if armor > 100 then
        armor = 0
    end
  
    SetPedArmour(PlayerPedId(), armor)
    Citizen.Wait(0)
  end
end)

A even better solution -> Runs at 216 ms

Citizen.CreateThread(function()
  local player = PlayerPedId()
  local armor = GetPedArmour(player)

  while true do
    armor = armor + 1
    if armor > 100 then
    armor = 0
    end

    SetPedArmour(player, armor)

    Citizen.Wait(0)
  end
end)
the examples are run at 50.000x faster than normal

But the question here is; would we really want to set armor every tick? A longer delay might suffice!

Try to keep that in the back of your mind!

DRY

Keep it DRY! Or in other words, don’t repeat yourself. Not only is this a good programming practice, but it sure is handy for gaining performance as well. I suspect this is fairly common sense, but there are resource out there that still seem to have issues with this. :disappointed_relieved:

Keeping it DRY also applies to performance, because we do not want to compute what we already have computed, instead we want to take the result from the previous computation instead of recomputing it again, simple stuff! :slight_smile:

Example

local pedindex = {}

function SetWeaponDrops()
    local handle, ped = FindFirstPed()
    local finished = false
    repeat 
        if not IsEntityDead(ped) then
                pedindex[ped] = {}
        end
        finished, ped = FindNextPed(handle)
    until not finished
    EndFindPed(handle)

    for peds,_ in pairs(pedindex) do
        if peds ~= nil then
            SetPedDropsWeaponsWhenDead(peds, false) 
        end
    end
end

Citizen.CreateThread(function()
    while true do
        Citizen.Wait(0)
        SetWeaponDrops()
    end
end)

A better way of going about it would be:

function SetWeaponDrops()
    local handle, ped = FindFirstPed()
    local finished = false 

    repeat 
        if not IsEntityDead(ped) then
            SetPedDropsWeaponsWhenDead(ped, false) 
        end
        finished, ped = FindNextPed(handle)
    until not finished

    EndFindPed(handle)
end

Citizen.CreateThread(function()
    while true do
        SetWeaponDrops()
        Citizen.Wait(500)
    end
end)

Conclusion

Hopefully this guide will give you some insight on why your resource might have performance issues. Again, if you still experience performance issues, please let me know, i’d appreciate it!

Good luck!

156 Likes

Thank you for this @Syntasu great way to stop the bitching of people. :slight_smile:

7 Likes

I really appreciate this as well. It sheds light well on common mistakes that I am also making.

8 Likes

Great guide! It may just be me, but it seems that the example for SetWeaponDrops() doesn’t work properly. I can still pick up AI weapons.

It was never meant to be functional, just a gist on how one might do so.

love it

2 Likes

(post withdrawn by author, will be automatically deleted in 24 hours unless flagged)

1 Like

by not doing retarded things in your code

Seriously, why are even asking… shouldn’t you be fixing your resource(s)!?!?
It’s not as simple as “disable the message”… that will not solve the issue…

Like stop being a short-sighted individual and actually solve the problem instead of ignoring it. This whole everything is fine aslong as it works mentallity aggrevates me, then people start loosing their shit when they are confronted with the facts

Me being a naive individual I am thinking that actually helping people instead of saying deal with it would make people stop asking such retarded questions. I was wrong.

HINT: It’s not fine!
26 Likes

You got me wrong.I appreciate you letting everyone know whats going on and how to fix the problem and I was just asking a question if you go in flames and start bitching about it go see someone about it.You guys updated the system and pushed all the shit on our shoulders now and now we have to “fix” tons of scripts what has been done by other scripters one by one.thank you!

2 Likes

You clearly don’t need to “take the blame from players” if you try to edit the resources or wait for the original developers to fix it. If you don’t feel like making sure only relatively decent scripts get added to your server, and have gameplay ruined for players with less capable PC specs, then yes, you are to blame.

1 Like

Anyways, this is not the place to dicuss the ethics of such system, instead let’s focus on actually fixing/improving stuff. Any random chatter why this systems sucks or why it should exist shall be punted off this topic.

2 Likes

Hey @Syntasu,

So we’ve got this script for locking doors on our server, it uses a function to drawtext on the object, and display it in 3D. If we increase the Citizen.Wait(0) to anything higher than 15, the text starts flashing.

How would you go about doing something like that? We still get the resource warning, if we increase it to 15 milliseconds, which is fair enough…

Regards

Do you mind sharing your code?

Only added a few more locations and changed something in the DrawText function.

1 Like

Seems like a typical case of stuffing everything into one thread kind of deal.

The guide suggests:

Segregate any code that does not require to be ran every frame. You can use for instance a global variable(s) to influence the code that is being ran every tick (without executing the expensive code).

The only code that needs to be ran every tick is indeed DrawText3d, all other logic in that loop can be ran at an other interval on another thread, this applies:

Think about a reasonable interval for a piece of code to execute at, for example you don’t need to check 60 times per second to check if a player died, instead consider using 1 or 2 times per second to check such a thing.

Common sense tells me that you do not have to check if a player is close to a door or check the door state every tick.

3 Likes

I’ll try and see if I can sort it out, cheers though.

I don’t know if it gets a bit messy unlocking the doors, if it doesn’t run on every tick, but it’s worth a test :slight_smile:

How do you test to see how long it takes a script to run in ms?

You can’t as far as I am aware of. Having some basic resource profiling tools would be neat. But I would not know how to go/implement such system :slight_smile:

@UnitedGaming @Syntasu you can use ETW for that.
Hit the Start Tracing button, play for a minute, than press Save Trace Buffers. You will see a trace has been added to the list below, right click on it and choose open trace in WPA.
In WPA choose FiveM.exe, expand “block” and expand “end”. You will be able to sort the resources by tick duration in ms.

2 Likes

That is only for client side resources. As I don’t have access to the server symbols :frowning: