• Tips for optimization
    27 replies, posted
You don't have to optimize this. I just want tips on how I should optimize it, so it doesn't lag a server. The server I'm running has been using a lot of RAM and CPU and I think it's my addon because it runs a lot of things in think. If you could tell me the logic of what I should do, that we be nice. Thank you for your time. [CODE] --[[ ©2016 Metamorphics (STEAM_0:1:52851671 is the one and only author) Covered by Attribution-NonCommercial-NoDerivatives 4.0 International http://creativecommons.org/licenses/by-nc-nd/4.0/ http://creativecommons.org/licenses/by-nc-nd/4.0/legalcode ]]-- AddCSLuaFile() SWEP.PrintName = "The Flash SWEP" SWEP.Author = "Metamorphics" SWEP.Instructions = "Right click to slow down time. Left click to punch. Sprint to run faster than ever before." SWEP.Category = "The Flash SWEP" SWEP.Spawnable = true SWEP.AdminOnly = false SWEP.ViewModel = "models/weapons/c_arms.mdl" SWEP.WorldModel = "" SWEP.Base = "weapon_base" SWEP.Primary.ClipSize = -1 SWEP.Primary.DefaultClip = -1 SWEP.Primary.Automatic = true SWEP.Primary.Ammo = "none" SWEP.Secondary.ClipSize = -1 SWEP.Secondary.DefaultClip = -1 SWEP.Secondary.Automatic = false SWEP.Secondary.Ammo = "none" SWEP.HoldType = "normal" SWEP.AccelerationMod = 1 /* Initialization */ function SWEP:Initialize() self:SetHoldType(self.HoldType) end hook.Add("GetFallDamage", "tfs_ChangeFallDamage", function(ply,speed) if ply.Speedster then return 0 end end) /* HUD */ function DoSpeed( MPH, isKPH ) if ( isKPH ) then return math.Round(MPH * 1.609344) .. " Kph" end return MPH .. " Mph" end function SWEP:DrawHUD() local speedNum = "0" if GetConVarNumber("tfs_mph",0) == 1 then speedNum = DoSpeed(math.Round(self.Owner:GetVelocity():Length() / 17.6), false) else speedNum = DoSpeed(math.Round(self.Owner:GetVelocity():Length() / 17.6), true) end local speed = tostring(SpeedNum) local percent = tostring(math.Round(self.Owner:GetNWFloat("TFS_Speedforce",1)*100)).."%" draw.SimpleText("Speed: "..speedNum, "Trebuchet24", 8, 8, Color(255,255,255,255), TEXT_ALIGN_LEFT, TEXT_ALIGN_CENTER ) draw.SimpleText("Speedforce: "..percent, "Trebuchet24", 8, 32, Color(255,255,255,255), TEXT_ALIGN_LEFT, TEXT_ALIGN_CENTER ) draw.SimpleText("RP Mode: FALSE", "Trebuchet24", 8, 56, Color(255,255,255,255), TEXT_ALIGN_LEFT, TEXT_ALIGN_CENTER ) end /* Deploy/Holster */ function SWEP:Deploy() if SERVER then self.Owner.Speedster = true end self.Owner:SetFOV(120, 0.5) end function SWEP:Holster() if SERVER then self.Owner.Speedster = false self.Owner:SetRunSpeed(400) if IsValid(self.Owner.tfs_trail_central) then SafeRemoveEntity(self.Owner.tfs_trail_central) end if IsValid(self.Owner.tfs_trail_right) then SafeRemoveEntity(self.Owner.tfs_trail_right) end if IsValid(self.Owner.tfs_trail_left) then SafeRemoveEntity(self.Owner.tfs_trail_left) end if IsValid(self.Owner.tfs_trail_hip) then SafeRemoveEntity(self.Owner.tfs_trail_hip) end return true end self.Owner:StopParticles() end /* Sound */ function SWEP:TFS_KeyCheck() if self.Owner:KeyPressed(IN_SPEED) then self.Owner:EmitSound(self.Owner:GetInfo("tfs_tosound","tfs_flash")) end end /* Attacks */ function SWEP:PrimaryAttack() self.Weapon:SetNextPrimaryFire(CurTime() + .1) local damage = 5 if self.Owner:GetVelocity():Length() <= 1500 then damage = 5 else damage = self.Owner:GetVelocity():Length()/200 end local trace = self.Owner:GetEyeTrace() if trace.HitPos:Distance(self.Owner:GetShootPos()) <= 75 then bullet = {} bullet.Num = 1 bullet.Src = self.Owner:GetShootPos() bullet.Dir = self.Owner:GetAimVector() bullet.Spread = Vector(0, 0, 0) bullet.Tracer = 0 bullet.Force = 5 bullet.Damage = damage self.Owner:FireBullets(bullet) end end function SWEP:SecondaryAttack() local Sm = self.Owner:GetInfoNum("tfs_slowmonum",0.2) self.Weapon:SetNextSecondaryFire( CurTime() + Sm/2 ) if SERVER then if game.GetTimeScale() == 1 then game.SetTimeScale(Sm) else game.SetTimeScale(1) end end end /* Acceleration */ function SWEP:SetSF() self.Owner:SetNWFloat("TFS_Speedforce",self.Owner:GetInfo("tfs_speednum")/100) if self.Owner:GetNWFloat("TFS_Speedforce",1) > 5 then self.Owner:SetNWFloat("TFS_Speedforce",5) end end function SWEP:Acceleration() local vel = self.Owner:GetVelocity():Length() if SERVER then if self.Owner.TFS_Tranq == false or self.Owner.TFS_Tranq == nil then if self.Owner:GetNWFloat("TFS_Speedforce",1) > 0.2 then self.Owner:SetRunSpeed(self.Owner:GetNWFloat("TFS_Speedforce",1)*2000) else self.Owner:SetRunSpeed(400) end else self.Owner:SetRunSpeed(400) end end end /* Water Running */ function SWEP:RunOnWater() if SERVER then local pos = self.Owner:GetPos() local tr = util.TraceLine( { start = pos, endpos = pos - Vector(0,0,5), filter = self.Owner, mask = MASK_WATER + CONTENTS_TRANSLUCENT } ) if tr.Hit and self.Owner:GetVelocity():Length() > 1500 then local dz = pos.z - tr.HitPos.z self.Owner:SetAnimation( PLAYER_WALK ) self.Owner:SetPos( pos + Vector(0,0,5-dz)) end end end /* Spawn Hook */ hook.Add("PlayerSpawn","tfs_reset", function(v) v.TFS_Tranq = false end) /* Lightning */ local TFS_LightningTable = {["tfs_yellow"]="yellow_lightning", ["tfs_red"]="red_lightning", ["tfs_green"]="green_lightning", ["tfs_orange"]="orange_lightning", ["tfs_blue"]="blue_lightning", ["tfs_purple"]="purple_lightning", ["tfs_black"]="black_lightning", ["tfs_white"]="white_lightning"} function SWEP:TFS_GetLightning() local TFS_color = "tfs_yellow" if TFS_LightningTable[self.Owner:GetInfo("tfs_lightningcolor")] then TFS_color = TFS_LightningTable[self.Owner:GetInfo("tfs_lightningcolor")] end self.Owner:SetNWString("TFS_LightningEffect", TFS_color) end function SWEP:AddLightning() local TFS_ent = self.Owner local TFS_Color = Color(self.Owner:GetInfoNum("tfs_red",255), self.Owner:GetInfoNum("tfs_green",255), self.Owner:GetInfoNum("tfs_blue",255), self.Owner:GetInfoNum("tfs_alpha",255)) local TFS_additive = false local TFS_startWidth = 20 local TFS_endWidth = 0 local TFS_lifeTime = 2 local TFS_textureRes = 1/(TFS_startWidth + TFS_endWidth)*0.5 local TFS_Texture = "trails/laser.vmt" local vel = self.Owner:GetVelocity():Length() local lightning_min = 1500 if vel > lightning_min then if self.Owner:GetInfoNum("tfs_trails",1) == 1 then if self.Owner:GetInfoNum("tfs_legacytrails",1) == 1 then if self.Owner:GetNWBool("TFS_CanPlayLightning",false) and self.Owner.Speedster then ParticleEffectAttach(self.Owner:GetNWString("TFS_LightningEffect","yellow_lightning"),PATTACH_POINT_FOLLOW, self.Owner ,0) self.Owner:SetNWBool("TFS_CanPlayLightning",false) end else if SERVER then if not IsValid(self.Owner.tfs_trail_central) then self.Owner.tfs_trail_central = util.SpriteTrail(TFS_ent, self.Owner:LookupAttachment("chest"), TFS_Color, TFS_additive, TFS_startWidth, TFS_endWidth, TFS_lifeTime, TFS_textureRes, TFS_Texture) end if not IsValid(self.Owner.tfs_trail_right) then self.Owner.tfs_trail_right = util.SpriteTrail(TFS_ent, self.Owner:LookupAttachment("anim_attachment_RH"), TFS_Color, TFS_additive, TFS_startWidth, TFS_endWidth, TFS_lifeTime, TFS_textureRes, TFS_Texture) end if not IsValid(self.Owner.tfs_trail_left) then self.Owner.tfs_trail_left = util.SpriteTrail(TFS_ent, self.Owner:LookupAttachment("anim_attachment_LH"), TFS_Color, TFS_additive, TFS_startWidth, TFS_endWidth, TFS_lifeTime, TFS_textureRes, TFS_Texture) end if not IsValid(self.Owner.tfs_trail_hip) then self.Owner.tfs_trail_hip = util.SpriteTrail(TFS_ent,
Not going to read your code, but in general: * If things are running fine, don't worry too much about optimization. * Identify which parts of your code take the most time, and only attempt to optimize those. Don't bother with the small stuff. There are tools that help you identify this, but you're gonna have to wait for another friendly poster to link to one of them as I don't remember :P * As much as possible, avoid functions that loop over a table to get a single piece of information out, such as [url=https://wiki.garrysmod.com/page/table/HasValue]table.HasValue[/url] (see Example 2 for how to avoid it), and any other inefficient functions.
Consider carefully WHEN things need to happen, at a cursory glance, I see you're calling ParticleEffectAttach() once, util.SpriteTrail at most 4 times, and doing a util.TraceLine() every tick. Consider if you really need to do all of these, or only need to do them once when certain conditions are met. I think NeatNit is talking about [URL="https://facepunch.com/showthread.php?t=1517058"]FProfiler[/URL] You might also consider reading over[URL="http://wiki.luajit.org/Numerical-Computing-Performance-Guide"] luaJIT's optimization guide[/URL]. (Although I would argue most of these are a bit overkill, and you would save more cycles looking carefully at what parts of the source engine are slow and avoiding those)
Thanks to both of you
Use atom's GLua linter, I've saw creators use it and so far it's been improving take TFA for example boosting fps on his weapon base by micro-optimization. [url]https://atom.io/packages/linter-glualint[/url]
[QUOTE=Lunaversity;51246828]Use atom's GLua linter, I've saw creators use it and so far it's been improving take TFA for example boosting fps on his weapon base by micro-optimization. [url]https://atom.io/packages/linter-glualint[/url][/QUOTE] is there a equivalent for Sublime?
[QUOTE=beeteegee;51246907]is there a equivalent for Sublime?[/QUOTE] doubtful all i remember is a syntax highlight for it.
I recently made thread about optimizations. Take a look inside, there are some good advices about optimization. [url]https://facepunch.com/showthread.php?t=1536929[/url] TL;DR until you're running extremely expensive functions each frame you're doing well because the engine optimises the code itself. anyway some advices: 1. move commonly used stuff to local scope. i.e. you're using LocalPlayer() in some function so you should run [I]player[/I] = LocalPlayer in beginning and use [I]player[/I] instead of LocalPlayer further 2. avoid networking tables 3. precache strings needed to be networked and send their IDs instead of strings themselves that's all I could remember it's late here :P
[QUOTE=igodsewer;51247151]I recently made thread about optimizations. Take a look inside, there are some good advices about optimization. [url]https://facepunch.com/showthread.php?t=1536929[/url] TL;DR until you're running extremely expensive functions each frame you're doing well because the engine optimises the code itself. anyway some advices: 1. move commonly used stuff to local scope. i.e. you're using LocalPlayer() in some function so you should run [I]player[/I] = LocalPlayer in beginning and use [I]player[/I] instead of LocalPlayer further 2. avoid networking tables 3. precache strings needed to be networked and send their IDs instead of strings themselves that's all I could remember it's late here :P[/QUOTE] Thanks! [editline]23rd October 2016[/editline] [QUOTE=igodsewer;51247151]I recently made thread about optimizations. Take a look inside, there are some good advices about optimization. [url]https://facepunch.com/showthread.php?t=1536929[/url] TL;DR until you're running extremely expensive functions each frame you're doing well because the engine optimises the code itself. anyway some advices: 1. move commonly used stuff to local scope. i.e. you're using LocalPlayer() in some function so you should run [I]player[/I] = LocalPlayer in beginning and use [I]player[/I] instead of LocalPlayer further 2. avoid networking tables 3. precache strings needed to be networked and send their IDs instead of strings themselves that's all I could remember it's late here :P[/QUOTE] How would I precache a string? Do you mean store it in a variable then send the variable?
[QUOTE=beeteegee;51246907]is there a equivalent for Sublime?[/QUOTE] Of course there is! [url]https://packagecontrol.io/packages/SublimeLinter-contrib-glualint[/url] Requires the SublimeLinter package.
[QUOTE=Metamorphics;51247267]Thanks! [editline]23rd October 2016[/editline] How would I precache a string? Do you mean store it in a variable then send the variable?[/QUOTE] Like this. Usually you send strings like that: [code] #server net.Start("message") net.WriteString("My message yeah!") net.Send(player) #client net.Receive( "message", function() local string = net.ReadString() print(string) end )[/code] But after we do precaching (might be calling this stuff wrong, sry :P) it it would look like this: [code] #server net.Start("message") net.WriteInt(1,32) net.Send(player) #client precachedstuff = {} precachedstuff[1] = "My message yeah!" net.Receive( "message", function() local stringid = net.ReadInt(32) print(precachedstuff[stringid]) end ) [/code] It's literally sending all string variables to client as he joins server. Benefits are: you are sending string only one time, easy translations, lower server load. You shall do that because each symbol in string is worth 2 bytes. Broadcasting not cached stuff (like DarkRP does) to players on small servers is ok but on big ones it equals to DDoSing yourself. By that you broadcast/send only digits referring to string, not strings themselves.
[QUOTE=igodsewer;51247470] You shall do that because each symbol in string is worth 2 bytes.[/QUOTE] No? Lua strings are single byte sequences, the networking is handled the same way.
[QUOTE=bigdogmat;51248016]No? Lua strings are single byte sequences, the networking is handled the same way.[/QUOTE] Yes. But the more symbols you have the longer sequence becomes. If you send long string to one client this might be ok. But if you broadcast string ie 30 chars long to 64 clients it becomes 30x64+1=1,920 bytes which approx equals to 1,9 kbytes. Now imagine if your server has tickrate of 8 kb/s how big perfomance drop would be when that message would be broadcasted. It's made same way on big servers such as SUP, some russian top 3 darkrp servers and on some others afaik. That's one of ways how they handle up to 127 slots without lags. UPD sry counted as 2 bytes for dat stuff for some reason :P but not sure if cyrillic and other non-latin chars encoded as 1 byte for each symbol need to test it out
[QUOTE=igodsewer;51248129]UPD sry counted as 2 bytes for dat stuff for some reason :P but not sure if cyrillic and other non-latin chars encoded as 1 byte for each symbol need to test it out[/QUOTE] Non-ASCII stuff is gonna be UTF-8. For cyrillic it IS 2 bytes per character, for other shit it might be 3 or even 4.
[QUOTE=igodsewer;51248129]Yes. But the more symbols you have the longer sequence becomes. If you send long string to one client this might be ok. But if you broadcast string ie 30 chars long to 64 clients it becomes 30x64+1=1,920 bytes which approx equals to 1,9 kbytes. Now imagine if your server has tickrate of 8 kb/s how big perfomance drop would be when that message would be broadcasted. [/QUOTE] Someone correct me if I'm wrong, but isn't networking done on a separate thread? This is going to have little to no impact within the actual game.
[QUOTE=mijyuoon;51248153]Non-ASCII stuff is gonna be UTF-8. For cyrillic it IS 2 bytes per character, for other shit it might be 3 or even 4.[/QUOTE] Thanks :3 [editline]23rd October 2016[/editline] [QUOTE=bigdogmat;51248160]Someone correct me if I'm wrong, but isn't networking done on a separate thread? This is going to have little to no impact within the actual game.[/QUOTE] Not sure because skilled guys from optimization thread such as SUP (right?) dev adviced to avoid networking tables. The only reason for that I could see is performance impact.
[QUOTE=bigdogmat;51248160]Someone correct me if I'm wrong, but isn't networking done on a separate thread? This is going to have little to no impact within the actual game.[/QUOTE] Network lag is arguably a lot worse than CPU lag.
[QUOTE=igodsewer;51248161] Not sure because skilled guys from optimization thread such as SUP (right?) dev adviced to avoid networking tables. The only reason for that I could see is performance impact.[/QUOTE] Networking tables is the same as networking any other type, only thing about using `net.WriteTable` is that it's bloated due to it using the biggest possible type for sending each value (e.g. sending all numbers as doubles even though it may always be an integer). Also considering it has to know which type it's sending, because of this it uses 4 values for each entire in the table. As long as you're smart about your data structures in such a way you can send the table in a predicted manner, you shouldn't be overly paranoid about it. Of course if your data can be stored client side without having to network it then you should as it's the correct thing to do.
[QUOTE=Lunaversity;51246828]take TFA for example boosting fps on his weapon base by micro-optimization.[/QUOTE] Micro-optimizations don't give FPS boosts; bottleneck elimination does.
[QUOTE=zerf;51249559]Micro-optimizations don't give FPS boosts; bottleneck elimination does.[/QUOTE] Micro-optimizations add up. But for something helpful.. One of the best things you can do is make use of shared files, you can avoid a lot of networking and make some pretty significant gains doing so.
[QUOTE=zerf;51249559]Micro-optimizations don't give FPS boosts; bottleneck elimination does.[/QUOTE] Micro-optimizations might not give a noticable boost individually, but consider that my weapon base is 20k lines *after* I linted away extra whitespace. It definitely adds up.
[QUOTE=FPtje;51247391]Of course there is! [url]https://packagecontrol.io/packages/SublimeLinter-contrib-glualint[/url] Requires the SublimeLinter package.[/QUOTE] thanks, i recall seeing this but i couldn't get to where it actually was
[QUOTE=StonedPenguin;51249632]Micro-optimizations add up. [/QUOTE] Counterintuitively, they don't. You can make a hundred separate pieces of code 1000x faster, but when they combined are 1% of the CPU time (which is usually the case), you will never notice the difference. Attack the bottleneck, however, and you'll begin to see genuinely huge differences. You just need to find out what the bottleneck is, and that's done with a profiler.
This thread hit critical mass a while ago, and although Falco already dropped some knowledge, I'm committed god damn it, there's no turning back now, so I'm going to go on another probably incoherent rant that will probably fall on deaf ears... [QUOTE=Apickx;51246095] I think NeatNit is talking about [URL="https://facepunch.com/showthread.php?t=1517058"]FProfiler[/URL] You might also consider reading over[URL="http://wiki.luajit.org/Numerical-Computing-Performance-Guide"] luaJIT's optimization guide[/URL]. (Although I would argue most of these are a bit overkill, and you would save more cycles looking carefully at what parts of the source engine are slow and avoiding those)[/QUOTE] +1 to FProfiler. Not used it myself but Falco is lit as fuck so it's probably at least as good as DarkRP. -1 to that LuaJIT optimization guide. You're right, it's overkill. A third of the things listed there don't apply to gmod because they're dealing with FFI (Which is also lit as fuck, if you're using Lua for a project you should totally consider using it!). The second third deal with really low level concepts that you shouldn't be worried about. If you're worried about loop unrolling or branch mispredicts in your SWEP then you're doing things very, very wrong. The final third are run of the mill micro-optimizations. Localizing everything and micromanaging variable scope will make your code ugly. Stop arguing about ipairs and numeric for, they do the same damn thing. Finally, a quote from that page itself: [quote]There are quite a few "easy" optimizations where the compiler is in a better position to perform them. Better focus on the difficult things, like algorithmic improvements.[/quote] [QUOTE=Lunaversity;51246828]Use atom's GLua linter, I've saw creators use it and so far it's been improving take TFA for example boosting fps on his weapon base by micro-optimization.[/QUOTE] [b]Yes! Use a linter! But don't use it for this![/b] I'm not going to argue that you can't get performance improvements from linting your code, but you should really be using it to make sure your code doesn't look like ass, not because you expect massive performance gains. No linter is going to transform O(N^2) code into O(logN) on a regular basis. Now I'm sure someone is going to give me a counter example and I'm going to have to shit myself. [B]Networking: Understand what you're networking and don't network more than you have to. This is a "no shit" kind of thing.[/B] [QUOTE=TFA;51249679]Micro-optimizations might not give a noticable boost individually, but consider that my weapon base is 20k lines *after* I linted away extra whitespace. It definitely adds up.[/QUOTE] How many of those lines are being executed frequently? Micro-optimize the stuff that gets called in a nested loop, every frame, if you micro-optimize anything.
[QUOTE=MadParakeet;51249868]How many of those lines are being executed frequently? Micro-optimize the stuff that gets called in a nested loop, every frame, if you micro-optimize anything.[/QUOTE] Sadly, quite a bit. I had multiply defined variables in GetViewModelPosition, Tick, and several other hooks. Additionally, I wasn't using scoping to its fullest extent, nor did I localize everything I could've. It also caught quite a few GetConVarNumber lines which were causing significant delays per-frame.
I wouldn't really call making a variable to avoid a repeated function call a micro-optimization. That's more good programming practice then anything.
[QUOTE=StonedPenguin;51250020]I wouldn't really call making a variable to avoid a repeated function call a micro-optimization. That's more good programming practice then anything.[/QUOTE] Regardless, it's something that'll get caught by a good linter. I don't make that mistake anymore, but a year or two can make a huge difference in experience.
[QUOTE=TFA;51250167]Regardless, it's something that'll get caught by a good linter. I don't make that mistake anymore, but a year or two can make a huge difference in experience.[/QUOTE] Calling functions multiple times isn't necessarily bad, especially in non-pure languages such as Lua. Linters cannot know when calling a function multiple times is bad. Functions have side effects. Each time you call them, something else may happen, even if you're calling them with the same arguments. A simple example is the random function. If you want two random numbers, you're gonna have to call the function twice. Would you want a linter to tell you that you should use the return value from the first call? A linter can make a very complicated analysis and may know in some cases when to warn, but it wouldn't be able to identify any practical situation. Go look up Rice's theorem for that one. The behaviour that cannot be statically determined would be "side effects".
Sorry, you need to Log In to post a reply to this thread.