Page 1 of 6 1 2 3 ... LastLast
Results 1 to 10 of 57

Thread: Performance & Memory monitoring

  1. #1
    Basic Member
    Join Date
    Sep 2017
    Posts
    56

    Performance & Memory monitoring

    Hiya. While working on my full replacement mod, it seems performance is naturally slowly degrading. Which I can tell both by the sudden inability to run higher timescales and through spam in my console that bots are taking too long. On top of that, I'm now getting strange lategame client crashes which may be because of memory issues. Dota dump gives me Exception codes: 0xE24C4A04 (?) and 0x80000003 (breakpoint reached) if that helps for devs

    Has anyone figured out a good way of monitoring where the performance and memories are appearing? Obviously I have some ideas what the heaviest code is, but with no way of quantifying it I may be missing important parts. Memory wise I'm completely in the dark since I don't think I store that much, yet there is a clear steady increase in VM memory usage.

    Additionally, has anyone figured out good rules of thumb regarding performance here? For example how the performance between API calls and regular lua calls compare and which API calls should be avoided in code that gets executed a lot?
    Last edited by Siesta Guru; 09-25-2017 at 03:16 AM.

  2. #2
    Basic Member
    Join Date
    Dec 2016
    Posts
    180
    I have same problem here in my code. when I remove my custom changes in full-bot overwrite, everything go in the right way. but when I revert changes, client performance become slow and hero_selection file is called rapidly till the end of game.

    Unfortunately, I have no exact idea or suggestion for you how to deal with it. however, I always try to remove or merge global variables as many as I can and use less local and global variables for every purpose in code. ANN algorithm is suitable for dota bots but with current dota client patch ... is awful.

    need more attention from valve developer to solve this issue.

  3. #3
    Basic Member
    Join Date
    Mar 2012
    Posts
    2,014
    Optimization is a very complex concept. If you are new to coding, it might not be a great idea to dive straight into code optimization without actually understanding the language first. Depending on your Lua knowledge, if someone says "do this, it's better", and you do it mechanically because someone says so, you might wonder why it either doesn't work or only works sometimes. While Lua is not the most complex language (take C++ as an example), this is solely because it's a scripting language (as opposed to C++ which is a programming language), but rest assured, it has its complications. Furthermore, keep in mind that optimization "by ear", done on code you just "see" without properly testing it might not have any benefit at all. The best approach is to first find the bottlenecks. Even 5000 optimizations in places where speed is hardly affected might result in MAYBE 2 extra FPS gain, while still having the low FPS of around 15. You must first search for the cause and fix it, not to treat the effect.

    In general, having low performance, performance degradation or execution latency might not only be the result of simple straightforward coding (i.e. unoptimized code - which believe it or not is not the cause of performance problems most of the time), but poor coding (i.e. using functions wrong, using expensive functions in time-critical spots, avoiding and breaking best-practices - which might or might not be intentional or just the coder might now know of any issues with the functions they use) or redundancy. All 3 combined are actually dangerous to the execution of a program (depending on what type of program it is or the level, it might actually result in a BSOD, but since we're talking about D2 here, the worst that can happen is running out of memory and making the CPU and GPU go tired because of useless instructions). Still, treating the bad code is not always the best "fix", since there might actually be a problem with the whole workflow, meaning the code design is poor.

    The best approach I found (please note an emphasis on I as there are a lot of better ways of doing this) is some trial and error, mainly, because I actually started to learn Lua since February, earlier attempts being just syntax familiarization. And rest assured, the first lessons were not optimizations
    After you learn the basics of Lua, you must learn the basics of the bot API. Learning the API is much more than knowing the function names and what parameters it has by heart. It's about learning the behavior (inner workings), knowing the execution cost and the performance cost. There are some functions that achieve similar things in the Lua core and the D2 bot API. A few might be better in a some situations, while the rest might be better in all situations (f.e. GetTeamMember() vs GetNearbyHeroes() vs GetAllUnits() vs GetBot()). Furthermore, there are tons of functions in both Lua and the D2 bot API that are written natively (in C or C++ depending on the case) which will always be faster than anything you write in Lua.

    Also note that copy-pasting code from other bots or from websites and putting them in your code is you just asking for FPS degradation. Writing a program doesn't work that way. Now a lot of people might joke about Windows 98 and its BSODs, but trust me, it wasn't a bunch of copy-pastes from Yahoo searches (there was no Google back then :P so yeah, believe it or not, people did survive and managed to live their lives without Google). If you need to have some code do something but don't know how and find some snippet somewhere, run it, and are just happy it works the way you want it to but have no idea what and how it does what it does, this is another possible source of problems (I found this in a bot once - and if you're curious, no, it didn't optimize anything because the coder didn't understand what both the code and the coder were supposed to do).

    Let's take an example. I noticed a lot of bots have a function that returns the opposing team id. It looks something like this:
    Code:
    function GetEnemyTeam ()
    	if (GetTeam() == TEAM_RADIANT) then
    		return TEAM_DIRE;
    	end
    	
    	return TEAM_RADIANT;
    end
    I saw it in at least 3 bots. It's not huge, there is little overhead, but it does have some instructions inside it. There is no problem with having a function like this in your utility file except... the D2 bot API already has GetOpposingTeam(). I will not get into a silly debate of the performance cost of a Lua function vs GetOpposingTeam(), but, it does add up. Native will always be faster and while this is not the supreme way to optimize it since this will never be the cause of 20 FPS drops, this is just about the principle: use native code when possible.

    Let's move on to something a bit more complex, but still, relatively simple. I asked Chris for a GetLocationToLocation() function, as well as others. Obviously since we need it and it's missing, we implemented it ourselves. This is what you will find in most of the bots:

    Code:
    function GetLocationToLocationDistance (v1, v2) 
        return math.sqrt((v1.x - v2.x) * (v1.x - v2.x) + (v1.y - v2.y) * (v1.y - v2.y) + (v1.z - v2.z) * (v1.z - v2.z));
    end
    Before we move on, let's talk about credits, licensing and permissions. No worries, I won't ask for $1000000 for this piece of code. Feel free to use it. It's just pure maths So, let's move on. This is taken from an older source of mine. You'll see it in a lot of bots, maybe with less parenthesis, other variables names, different order, whatever. Basically, it's the same, because that's how it's calculated. There is no magic behind it. Speed-wise, it's relatively fast. Multiplications and Additions are always faster than other operations. But do we stop here? No. There are other things we see there. Let's break it down:
    • Additions: 2
    • Subtractions: 6
    • Multiplications: 3
    • Calls: 1 internal (math.sqrt), 1 external (GetLocationToLocationDistance): 2 in total
    • module access: 1 (math.sqrt)
    • execution: 2 locals, 1 call, 9 operators, 1 return so far
    • math.sqrt will perform a square root operation which is quite expensive (no, it won't kill a CPU, it's expensive relative to an addition), therefore the operations and calls increase


    The advantage is that math.sqrt is native. There is no sqrt code in Lua. Just an external call to C. Still, as you can see, even a basic maths function has some overhead. As I said, it won't kill a CPU, but depending how often you call it and where, you might have a few surprises.
    Code needing this can be SOMEWHAT optimized. This was a request of mine that was added for GetUnitToLocationDistance() and GetUnitToUnitDistance(): using square distances. That basically means, we removed the sqrt call and instead of checking if the distance from unit X to area Y is 100, we check if the distance is 10000 (100 squared ).
    Not ideal, but still better. However, GetLocationToLocationDistance() is not implemented natively. And then I found the Vector() implementation.

    Code:
    function GetLocationToLocationDistance (v1, v2) 
        return (v1 - v2):Length();
    end
    Now we only perform one subtraction in Lua (which expands in 3 native subtractions between the vectors) and all other operations are done natively. This will help quite a bit. This can even be further optimized by calling Length2D instead of Length(). But again, don't just copy paste the code. Understand it. Compare the differences between the Lua and C version. Study the Vector class (note The link I provided should only be used for the Vector definition; the API specified there can only be used for custom games/maps and cannot be used by bot API; the Vector link there is solely for explanatory reasons, because it is not defined in the bot API or anywhere else). Understand the difference between Length() and Length2D() and what the advantages and disadvantages of both are.

    But, let's move on to actual best practices. There are tones of materials online, but the issue is identifying which you need. Some are from Lua 4 which is ... old. D2 uses Lua 5.1. There are a few optimizations done in 5.2 but nothing that can double FPS in D2.

    Let's start:


    Other advices:
    • avoid iterations when you can
    • avoid creating stuff inside iterations when you can
    • avoid recreating stuff when possible (especially closures !!!!)
    • remove debug prints in the console or on the screen in the final production code that you upload to the workshop; it's not only about cosmetics, but also debugging adds quite some overhead
    • cache almost everything you can; keep in mind caching it in one frame, but recalculating everything again in the next might not help too much; depending on your code design there might be other needs to improve caching (see nostrademous' global hero data caching in his FullOverwriteBot for an example of how to cache in between calls and frames)
    • if someone says doing XXXX will be better and optimize, don't just do it, test it first. If it won't be better it doesn't necessarily mean that the code is wrong or the person is wrong, just that the use-case doesn't apply to you; also saying "well I'll just keep it then, it can't hurt" should only apply if you tested that the code workflow is unchanged (see the regression testing issue I raised)
    • when testing optimizations, the usual for 1 -> 10000000 loop on an idle PC might not be the best test case; you can't compare some addition in a loop with iterating through 500 units each frame while the computer is also drawing every frame with HD resolutions
    • avoid redundancy
    • avoid code bloating
    • heavy math operations (especially those based around division) and string manipulation are expensive; calling them each frame can decrease performance
    • use modular code, but don't split code too much; having 6 files, each with 2 functions is overkill
    • avoid global functions and variables as much as possible


    Conclusion: everything costs. EVERYTHING. Every line of code has its own overhead. Even declaring a local variable and assigning it nil. Yes, it might be infinitesimal but it's there and it adds up. While some don't use up cpu power, they might use up memory. For example, table creation. Why? It's an object. Objects generally use up memory. CPU is generally used up when performing calculations and executions.

    Further reading that I recommend which has nothing to do with optimization but has everything to do with optimization:


    Sorry but if you expected advice like "show me your code! Ah, do this and it will be 500% times better", you won't find it. Optimizing is a lot of work, because changing the code's design or the process workflow might produce bugs. Therefore, serious unit tests must be done. If they work right, you need some regression testing to make sure that the changed code works well under all circumstances. For example, some people code bots for both teams, but when testing they only test on Radiant. We had some issues with the API when it came to Dire, believe it or not. It showed up late cos most tested on Radiant and the actually players (not the coders) spotted the differences. While testing is something for players, let's be serious now, throwing some copy-pasted bot on the workshop and claiming "you did it" but it has a bunch of issues because it's just an untested carbon copy bunch of lines off forums or other bots is in no way a good image for someone.

    Also, when writing code, design is the most important thing from a workflow point of view, but when it comes to data quality, standards are the most important thing. Try to define some coding standards for yourself. These are some examples, but obviously, not a golden bible. But they might serve as a starting point
    When you start using a standard and stick to it, mistakes are easier to be noticed. Think: why does a compiler or interpreter find not only syntax errors, but also possibly overflows or other things that could happen at run-time and it should now know about, but does? Because of standards Even the syntax of a language is a predefined standard, otherwise it results in a compile error. If everyone would write code using whatever words they'd like instead of keywords, it'd be chaos...
    Last edited by The Nomad; 10-01-2017 at 10:52 PM.
    Explanations on the normal, high and very high brackets in replays: here, here & here
    Why maphacks won't work in D2: here

  4. #4
    Basic Member aveyo's Avatar
    Join Date
    Aug 2012
    Location
    EU West
    Posts
    2,924
    Everything said by The Nomad is good.
    If only Valve developers themselves followed that. Dota lua & vscript is horrible by itself. Back-end C++ ain't no better either. Dedicated servers still crap themselves after x entities, x particle effects, x xxx.
    There is a total lack of idiot-proofing, unforgivable in the context of having this pile of crap opened for amateur custom game developers and now, bot script creators.
    Even if you were to write perfect code despite the lack of documentation, it will still underperform.
    Garbage collecting seems inexistent and so memory leaking and performance degradation is the norm, not the exception.

  5. #5
    Basic Member
    Join Date
    Mar 2012
    Posts
    2,014
    Quote Originally Posted by aveyo View Post
    Everything said by The Nomad is good.
    If only Valve developers themselves followed that. Dota lua & vscript is horrible by itself. Back-end C++ ain't no better either. Dedicated servers still crap themselves after x entities, x particle effects, x xxx.
    There is a total lack of idiot-proofing, unforgivable in the context of having this pile of crap opened for amateur custom game developers and now, bot script creators.
    Even if you were to write perfect code despite the lack of documentation, it will still underperform.
    Garbage collecting seems inexistent and so memory leaking and performance degradation is the norm, not the exception.
    Thank you for the feedback

    While the Lua GC is pretty good, it is still relatively slow due to its intense reference searcher (unlike most GCs, it can actually detect orphans ) and because of this the timing might be so set-off that it can take a long time until the next iteration and therefore you have to stick with the current bloating until Lua "decides" it's time to clean-up. Forcing a GC call is also not recommended. While it won't make THAT much of a difference, there might be some benefit to some new events that trigger on init (such as pseudo-constructors) and clean-up (such as destructors). ATM everyone is forced to do event checks in Think (luckily Chris started adding some to help us) but there are still so many missing... So I agree with you there!

    I am delving into some debugging techniques to see how much help they can be and see if further research is worth it, since ATM any profiling and code analysis is done natively and D2 restricts native modules. Obviously even the best profiler I'd write will result in some heavy performance degradation. I tested a simple one to see what it can do and it killed 10 FPS. I'll try writing one to see how much it'd help but if D2's LUA VM is slow as hell, then any profiling will be worthless since it will just analyze the degradation done by the profiling ...

    Still, I'll give it a try... Might reach a dead end though. Lua executed in VScript is not only slow because it's Lua, but also, because it has to keep up with the D2 engine (and it will never be able to).

    I am still optimistic as the bot API is in its infancy. This year was just a run-about-test. There is no way to actually write a serious bot from scratch that is pure-Lua for now. I mean I don't think we have one that can even beat the native bots as a start.
    Let's hope that web ML might improve this, but speed-wise, it'll have a major setback. I fear that at some point there will be no other way than to use native code. And that might open some dangerous doors
    Last edited by The Nomad; 10-01-2017 at 08:23 AM.
    Explanations on the normal, high and very high brackets in replays: here, here & here
    Why maphacks won't work in D2: here

  6. #6
    Basic Member aveyo's Avatar
    Join Date
    Aug 2012
    Location
    EU West
    Posts
    2,924
    Why would you bother profiling when Valve is not?
    They have steam charts. Yet the cpu and bandwidth quota, and the timeouts for loading and connecting to custom games (including the 8+ months in development by Valve - Siltbreaker) is set as if everybody has octa-core 4Ghz CPU, 16GB RAM, 1TB SSD and 2GB fiber connection with 16ms to Seattle.
    Just pray your users have at least lots of RAM so the game won't crash. Degrading performance over (short) time is fine - just look how launching Dota, then activating game ui and going back in-game takes some of your fps never to return it until relaunch.

    And what web ML, isn't http request limited to post, and you have to dance around with the filesystem to achieve a limited 2-way communication ?
    At least that's what someone stated when I enquired about it. I find it baffling. Just get the http request stuff fully functional, limit it if you need so, but allow developers to slap a rest application on their pc / use distributed computing / whatever / to make bots that do not suck. Else why would you showcase on TI a great 1vs1 SF bot that is technically impossible to contest with the current bot api?

    And please, just drop broken bots from the workshop. Anticipate mixed-bot-scripts scenario. Kill the ones skyrocketing the VM to 2GB+ even faster than some (broken) custom games. Because we all suffer from it on our daily dotes. Servers running bots vs bots with higher priority than people vs people. Thanks Gaben.

    Edit: back to OP - you've said it best "performance is naturally slowly degrading" - it will do so despite your best efforts.
    Optimization will only slow it more, not cure it. Save that for last.
    You only need to make sure your script will not crash the game after a long session (90-120minutes) while running on a 32bit machine. Keep in mind the user might have onboard graphics so VM reaching 1GB is not ok.

  7. #7
    Basic Member
    Join Date
    Mar 2012
    Posts
    2,014
    Quote Originally Posted by aveyo View Post
    WAnd what web ML, isn't http request limited to post, and you have to dance around with the filesystem to achieve a limited 2-way communication ?
    At least that's what someone stated when I enquired about it.
    That was me I don't think that changed. Or at least no word from Chris on that.

    There were some posts saying players can't play bots on dedicated servers. My guess is some either crash or use all the memory and the servers are out... so yeah, a workshop cleanup should happen.
    Explanations on the normal, high and very high brackets in replays: here, here & here
    Why maphacks won't work in D2: here

  8. #8
    Basic Member
    Join Date
    Sep 2017
    Posts
    56
    Ah, that's a wonderful guide for general performance The Nomad, thanks for taking the time to write all of that out

    It indeed tends to be the huge of heavy functions and additional loops etc that kills performance most quickly rather than the lack of clever optimization tricks, but it can be quite a challenge to actually find which parts are the most draining if you don't know the approximate costs of individual elements. without that kind of knowledge, refactoring becomes rather hard. And in some cases the small bits actually make a huge difference, though it's best to do that later on in the project. Programmer performance is generally the most important performance metric

    Anyway, meanwhile I've discovered a way to calculate the approximate time necessary to execute statements. As some of you may've already tried, the RealTime() call doesn't have high enough precision to be able to just subtract the time before and after some code to tell how long an execution really takes. But as it turns out, the increase of the value of RealTime() does actually relate to the real time and can change during a bots execution. To be precise, the chance that it jumps up in value by its smallest possible steps relates to how long the code takes to execute. So basically, just take the RealTime() before your code, then take it after, add the difference to a summed variable and keep track of how many times you did this. The average taken over a couple of thousand of game frames will you tell you how long a method takes in high detail.

    As it turns out using that method, one part I expected to take over half of my execution time actually took less than 1%, while another function used up much more time than anticipated.
    Anyway, with that technique we can calculate how long some c++ functions take. For example, on my hardware, taking different versions of the GetUnitToLocationDistance
    c++ version, 1000 calls takes 0.397 ms
    lua 2D distance with precached location value, so: Dist2D(loc1,loc2) 1000 calls takes 0.516 ms
    lua 2D without precached loc, so: Dist2D(bot:GetLocation(),loc2) 1000 calls takes 0.782 ms
    lua 3D precached 1000 calls takes 0.728ms
    lua 3D without precache, 1000 calls takes 1.007ms



    Some quick other checks revealed:
    bot:GetNearbyCreeps(1600,false) -> 0.55 ms for 1k calls
    GetUnitList(UNIT_LIST_ALL) -> 5.02 ms for 1k calls (early game)
    IsRadiusVisible(loc,1000) -> 0.18 ms for 1k calls
    GetLaneFrontLocation(GetTeam(),LANE_MID,0) -> 0.48 ms
    GetLinearProjectiles() -> 0.22 (no projectiles afaik)
    GetAvoidanceZones() -> 0.23
    for i, someval in pairs(sometable) do i = i + 1; end -> 0.0855 (table has size 10)

    Keep in mind though that there's going to be some overhead because of the way I'm registering the time.

    It seems luas performance is actually quite good compared to c++ so it isn't too big of a deal to use lua methods. It's also interesting to me that bot:GetLocation() seems surprisingly heavy compared to the distance calculation. There may be a small performance hit to get through the binding to the c++ code. And so caching these types of values in lua is a good idea inside of nested loops if you're struggling with performance.
    The lua loop result actually has very minimal costs, so it really seems that in most cases you're probably actually better off just using lua logic. Rather than calling GetUnitList(UNIT_LIST_ALL) several times, cache it internally if convenient


    I am still optimistic as the bot API is in its infancy. This year was just a run-about-test. There is no way to actually write a serious bot from scratch that is pure-Lua for now. I mean I don't think we have one that can even beat the native bots as a start.
    It's quite possible. Mines a full rewrite in lua and it usually beats the native bots. There's a bunch still missing from the api obviously, but we can go pretty far with what we've got
    Last edited by Siesta Guru; 10-01-2017 at 04:01 PM.

  9. #9
    Basic Member
    Join Date
    Mar 2012
    Posts
    2,014
    Quote Originally Posted by Siesta Guru View Post
    Ah, that's a wonderful guide for general performance The Nomad, thanks for taking the time to write all of that out
    I hope you will find it useful
    Quote Originally Posted by Siesta Guru View Post
    It indeed tends to be the huge of heavy functions and additional loops etc that kills performance most quickly rather than the lack of clever optimization tricks, but it can be quite a challenge to actually find which parts are the most draining if you don't know the approximate costs of individual elements. without that kind of knowledge, refactoring becomes rather hard. And in some cases the small bits actually make a huge difference, though it's best to do that later on in the project. Programmer performance is generally the most important performance metric

    Anyway, meanwhile I've discovered a way to calculate the approximate time necessary to execute statements. As some of you may've already tried, the RealTime() call doesn't have high enough precision to be able to just subtract the time before and after some code to tell how long an execution really takes. But as it turns out, the increase of the value of RealTime() does actually relate to the real time and can change during a bots execution. To be precise, the chance that it jumps up in value by its smallest possible steps relates to how long the code takes to execute. So basically, just take the RealTime() before your code, then take it after, add the difference to a summed variable and keep track of how many times you did this. The average taken over a couple of thousand of game frames will you tell you how long a method takes in high detail.

    As it turns out using that method, one part I expected to take over half of my execution time actually took less than 1%, while another function used up much more time than anticipated.
    Well, first off, DotaTime(), GameTime() and RealTime() should show the same delta in theory. The condition is as long as the game is not paused (as only RealTime() moves).
    What the Time() functions are, are a series of results which represent the time between the current and the last frame. In simple terms: it shows how long it took for the current frame to draw. This, of course, is affected by a number of factors, from resolution, to the number of entities it has to draw and to internal calculations (such as the backend code). This is how FPS is mainly affected. When playing online, such calculations are affected by the ping as well (and vice versa). Adding up the deltatime will, in time, equal a second (with the proper floating points, so it will never be a round number, except maybe by accident). On Windows machines, most apps usually use QueryPerformanceCounter().

    Because of this behavior, when you check Time() functions you see a discrepancy between consecutive deltatimes (fact is, consecutive deltatimes will never be equal except by accident). While checking the RealTime() will not measure the actual discrepancy, because this is not the root, but the effect, it can only be used as a reference. Profiling is the only surefire way to measure bottlenecks.

    Quote Originally Posted by Siesta Guru View Post
    Anyway, with that technique we can calculate how long some c++ functions take. For example, on my hardware, taking different versions of the GetUnitToLocationDistance
    c++ version, 1000 calls takes 0.397 ms
    lua 2D distance with precached location value, so: Dist2D(loc1,loc2) 1000 calls takes 0.516 ms
    lua 2D without precached loc, so: Dist2D(bot:GetLocation(),loc2) 1000 calls takes 0.782 ms
    lua 3D precached 1000 calls takes 0.728ms
    lua 3D without precache, 1000 calls takes 1.007ms



    Some quick other checks revealed:
    bot:GetNearbyCreeps(1600,false) -> 0.55 ms for 1k calls
    GetUnitList(UNIT_LIST_ALL) -> 5.02 ms for 1k calls (early game)
    IsRadiusVisible(loc,1000) -> 0.18 ms for 1k calls
    GetLaneFrontLocation(GetTeam(),LANE_MID,0) -> 0.48 ms
    GetLinearProjectiles() -> 0.22 (no projectiles afaik)
    GetAvoidanceZones() -> 0.23
    for i, someval in pairs(sometable) do i = i + 1; end -> 0.0855 (table has size 10)

    Keep in mind though that there's going to be some overhead because of the way I'm registering the time.
    What does "precached location" mean? Do you mean a local variable? Honestly, I don't really expect the Get*Distance() functions to be that intensive. When things get hot, you can use, as I said, GetUnitToLocationDistanceSqr() and GetUnitToUnitDistanceSqr() which will decrease some of the performance hits.
    The functions you listed are very simplistic so they shouldn't take more than 0.5 miliseconds per average as your tests showed. GetUnitList() is one of the most expensive functions in the API but that is obvious because of how it works It doesn't use any cached info, it calculates everything real time and iterates through all units. The fact it takes 5 ms for 1k calls is actually quite good considering what it does.

    Quote Originally Posted by Siesta Guru View Post
    It seems luas performance is actually quite good compared to c++ so it isn't too big of a deal to use lua methods. It's also interesting to me that bot:GetLocation() seems surprisingly heavy compared to the distance calculation. There may be a small performance hit to get through the binding to the c++ code. And so caching these types of values in lua is a good idea inside of nested loops if you're struggling with performance.
    The lua loop result actually has very minimal costs, so it really seems that in most cases you're probably actually better off just using lua logic. Rather than calling GetUnitList(UNIT_LIST_ALL) several times, cache it internally if convenient
    Actually, I have a theory about GetLocation() and I'd like Chris to look into it if possible. It seems GetLocation() creates a new vector Lua (it's most likely the same object in the C++ backend) object on each call, even if the unit doesn't move. I am fairly sure optimizing this might help a lot since a unit's location is critical and important in just about any part of the code. It's inevitable to be called each frame in multiple places and on multiple units at once. Binding is most likely done in C as a wrapper, with a C++ internal call. That should not be such a heavy hit on performance, compared to the real-time code interpretation in VScript's VM.
    Loops have indeed improved. while() do is the slowest. for int, low, high do is the fastest and for pairs is somewhere in between. Obviously the int loop will only work on indexed lists/int arrays. pairs() can work on any table.

    Pure calculus functions will obviously be faster than those returning tables, but since Lua is mostly centered around tables, the code is optimized quite well. Table operations other than indexing/access will be more expensive though (sort being one of the heavy hitters).

    Keep in mind that simple checks on Projectile() functions will not show much. Imagine a 5v5 fight with projectiles, iterating through each table, possibly twice (depending on your code, you might actually iterate it once for allies and once for enemies), each frame, for as many seconds (or minutes) as the fight takes place. Also keep in mind for things like Macropyre, native avoidance zones are also created. So there is a lot more going on behind the screens than just drawing, game mechanics and the API filling tables for projectiles. The avoidance zones are just an example. There is more. For example, GetNearby() functions use a cached grid info which gets updated regularly, I estimate either each frame or every 300 ms. Then a distance check is performed on all units, AFTER the internal checks (such as visibility and some others, like the type of unit to filter out and so on). And if I am not mistaken (Chris should correct me), all of this is updated even if you don't need it. I don't think the nearby 1600 map-unit grid is cached on your call, but rather non-stop and the call just filters out the results, otherwise, it would not be actual caching
    Explanations on the normal, high and very high brackets in replays: here, here & here
    Why maphacks won't work in D2: here

  10. #10
    Basic Member
    Join Date
    Dec 2016
    Posts
    46
    Quote Originally Posted by The Nomad View Post
    Let's take an example. I noticed a lot of bots have a function that returns the opposing team id. It looks something like this:
    Code:
    function GetEnemyTeam ()
    	if (GetTeam() == TEAM_RADIANT) then
    		return TEAM_DIRE;
    	end
    	
    	return TEAM_RADIANT;
    end
    I saw it in at least 3 bots. It's not huge, there is little overhead, but it does have some instructions inside it. There is no problem with having a function like this in your utility file except... the D2 bot API already has GetOpposingTeam(). I will not get into a silly debate of the performance cost of a Lua function vs GetOpposingTeam(), but, it does add up. Native will always be faster and while this is not the supreme way to optimize it since this will never be the cause of 20 FPS drops, this is just about the principle: use native code when possible.
    .
    int GetOpposingTeam( nLane, fAmount )

    Returns the opposing Team ID.
    The Wiki said that, so I don't understand what the parameter is. Maybe this is a wiki writing error.


    Quote Originally Posted by The Nomad View Post
    Not ideal, but still better. However, GetLocationToLocationDistance() is not implemented natively. And then I found the Vector() implementation.

    Code:
    function GetLocationToLocationDistance (v1, v2) 
        return (v1 - v2):Length();
    end
    Now we only perform one subtraction in Lua (which expands in 3 native subtractions between the vectors) and all other operations are done natively. This will help quite a bit. This can even be further optimized by calling Length2D instead of Length(). But again, don't just copy paste the code. Understand it. Compare the differences between the Lua and C version. Study the Vector class. Understand the difference between Length() and Length2D() and what the advantages and disadvantages of both are.
    I don't think "Dota_2_Workshop_Tools/Scripting/API" function and class can called by bot script, it can only be used for workshop map.

    After all, I think your experience is very valuable.
    Last edited by adamqqq; 10-01-2017 at 10:03 PM.

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •