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
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!
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 , 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.
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!
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!