Optimizing Code

Discussion in 'Resources' started by Technius, May 30, 2012.

Thread Status:
Not open for further replies.
  1. Offline

    Technius

    I've recently come upon some plugins that take up massive amounts of memory. That is really bad, because it brings down the overall performance of servers that run them. So I've come up with some tips on how to optimize your code. Just don't optimize too much, as you can end up with messy code that ends up taking more of your time.

    Tip 1: Dealing with Lists, Maps, etc.
    Most Lists, Maps, etc. come with clear and remove methods. Remove data might save a little more memory, but every byte counts! Most plugins load data when enabled, so why not unload data when disabled? If a player joins and data is added, try not remove the data when the player quits? Here's some examples of what to do and what not to do:
    Do:
    Code:
    @EventHandler
    public void join(PlayerJoinEvent event)
    {
        somemap.put(event.getPlayer(), new Object());
    }
    @EventHandler
    public void quit(PlayerQuitEvent event)
    {
        somemap.remove(event.getPlayer());
    }
    Do not:
    Code:
    @EventHandler
    public void join(PlayerJoinEvent event)
    {
        somemap.put(event.getPlayer(), new Object());
    }
    //Storing a player can cause memory leaks, so watch out!
    
    Tip 2: Declare a variable if you're using a "getter" for the same variable
    I have read that using "getter" methods for the same variables multiple times take up more processing speed and/or memory. It might not be true, but it does make for cleaner code. I'll give you some examples:
    Do:
    Code:
    @EventHandler
    public void asdf(PlayerInteractEvent event)
    {
        Player player = event.getPlayer();
        player.sendMessage("a");
        player.sendMessage("a");
        player.sendMessage("a");
        player.sendMessage("a");
        player.sendMessage("a");
    }
    Do not:
    Code:
    @EventHandler
    public void asdf(PlayerInteractEvent event)
    {
        event.getPlayer().sendMessage("a");
        event.getPlayer().sendMessage("a");
        event.getPlayer().sendMessage("a");
        event.getPlayer().sendMessage("a");
        event.getPlayer().sendMessage("a");
    }
    Tip 3: Don't use imports if you reference a variable only a few times
    If you're using something like Listener only once, you don't have to import it. You can access it by just typing packagename.classname. For example, to reference Listener without importing it, I would type org.bukkit.event.Listener. It's a bit longer to type, but it does save you one line.
    Example:
    Code:
    public class SomeListener implements org.bukkit.event.Listener

    I hope these tips help you!

    Note: I switched from syntax tags to code tags because edits kill formatting. Fix this, XenForo!
     
    Photon156 likes this.
  2. Offline

    Hidendra

    Re tip 2, the overhead of methods are trivial. But your point still stands and is a good point, as if you're going to keep re-using an object from a getter, just assign it to a local var ! :)

    Re tip 3, imports are only used by the compiler, so it doesn't make a huge difference. IDEs will handle imports for you automatically (Intellij can remove ones you aren't using automatically, no clue if Eclipse can). Most IDEs will hide imports, so unless you're in Notepad, they won't get too long.

    Thought I'd add this too: Do not prematurely optimize -- it's a common saying but it holds water. "Optimizing" can add a lot of confusing code and even slow things down. So profile your code and test to ensure it's actually the bottleneck before making it more complex then it needs to be! In my own experience, prematurely optimizing came to bite me in the ass down the road because of unjustified complexity which turned out to be moot

    edit: just to clarify the above point, I don't mean to not optimize anything prematurely. Each tool is different for each job, so if you're trying to use an ArrayList for something that needs to be searched in constant time, you've made a serious design mistake. But if you have, say, a method that has 3 nested loops in it (for whatever reason), but can make it only 2 loops by making it 100 lines longer, it would be beneficial to make sure you can actually benefit from that added complexity by testing first.

    An excellent book one can read is Code Complete, it goes into actual code construction and various other things in a very detailed, research-oriented and awesome way (it uses proven research, not the author's opinion). It forced me to think much differently about how I program, and I'm not even finished reading it ;)
     
    Technius likes this.
  3. Offline

    Acrobot

    Hidendra
    Yep, absolutely - DON'T OPTIMIZE TOO MUCH. It needed to be in all-caps. When I started working on ChestShop 3, I was probably thinking that more optimisations = better code. Nope. Now I have to do twice the amount of work to bring the code to a "nice", readable and usable state.

    I can also recommend "Clean Code: A Handbook of Agile Software Craftsmanship".
    http://www.amazon.com/exec/obidos/ASIN/0132350882/
    I have it (unfortunately a translated version, so it is sometimes confusing for me, because I do most of my programming in English, so I also learn it in English)
    The book is really great - some of the chapters might feel a bit obvious, other ones might feel "heavy", but it's a really great book.
     
  4. Tip one - What your trying to say is completely correct, but what your actually doing is wrong. NEVER store a Player object outside of a local var. When the player logs off or dies, the object becomes null, and it can lead to memory leaks. Store the players name, or even better it's entity ID.
     
  5. Offline

    Technius

    Hidendra Acrobot Good points. I'll add them in there.
    tips48 It's being removed from the list so it doesn't reference to a null or garbage-collectable object.
     
  6. Ahh, missed that..I still wouldn't recommend it, although that should be fine. Definitely a no go storing them outside a collection though. Too many issues to make it viable.
     
  7. Offline

    ZachBora

    Eclipse gives you a warning on unused imports
     
  8. And will remove them as well as resolving import errors with CTRL-O :)
     
  9. 1: use strings instead of Players, more safe in case of errors or that the plugin wil get disabled, an misses the events of disconnect
     
  10. Offline

    Cirno

    You can set multiple variables of the same type like so:
    Code:JAVA
    1. String x = "a", y = "b", z = "c";

    instead of
    Code:JAVA
    1. String x = "a";
    2. String y = "b";
    3. String z = "c";
     
  11. Offline

    ZachBora

    Not as easy to read. But is it faster? I doupt it makes a difference.
     
  12. Offline

    Cirno

    *doubt

    It's a little faster and a little lighter on ram usage, for what ever reason. I just noticed that when I was testing a plugin, and thought "oh screw it" and just did that and noticed a slight (like 1%-1.5%) less ram usage.
     
  13. Offline

    ZachBora

    I can't find anywhere on the internet that it'd increase speed and save RAM.
     
  14. Offline

    desht

    Sorry, but you're making stuff up. "I noticed" is not a valid benchmark test by any standards. There is absolutely no difference in the generated bytecode between code that sets variables on three lines and code that sets variables on one line - you can test that for yourself with javap if you don't believe me.
     
    Bone008, ferrybig and jtjj222 like this.
  15. Offline

    Rahazan


    A few other tips if you want to save nanoseconds:
    - Two one-dimensional arrays are a lot more efficient than one two-dimensional array.
    - Try to use primitives instead of their Object counterpart (int instead of Integer).
    - Try to allocate as little memory as possible; create as little objects as possible.
    - If you use one field in a specific Object a lot of times, consider making it public instead of using a getter. Do keep in mind that this is not "neat" programming. (3 to 7 times faster).
    - Use static final int's instead of enums. Like the previous point, negligible difference.. There are a few advantages to enums however, such as being type-safe.
    - Make methods that do not access an object's fields static, this'll, apparently, save you 15-20% of the already negligible time needed to make the method call.
    - Use static final for constants.
    - Don't use enhanced for-loops for ArrayLists, hand written, counted loops are 3 times as efficient.. I am sure most programmers are too lazy to do this & do not care about nanoseconds value the readability of enhanced for-loops.

    Sources: Teachings at uni, Android documentation and robotics courses..
     
  16. Did not know that and I got to testing it... it seems true, however, I also noticed something else... if you're using List<String> instead of ArrayList<String> it's slower :confused:
    Code:
    	private List<String>	test	= new ArrayList<String>();
    	
    	public void onEnable()
    	{
    		for(int i = 0; i < 100000; i++)
    		{
    			test.add("" + i);
    		}
    		
    		System.out.print("Size = " + test.size());
    		
    		long start = System.currentTimeMillis();
    		
    		for(int i = 0; i < test.size(); i++)
    		{
    			test.get(0);
    		}
    		
    		System.out.print("Time: " + (System.currentTimeMillis() - start));
    	}
    This one prints 2-3, setting List to ArrayList prints 0-1.
     
  17. Offline

    desht

    Right, this is because an enhanced for-loop creates an implicit iterator on the collection. Calling the iterator's next() method has slightly more overhead than just incrementing an index. However, the performance difference is tiny (a few nanoseconds per iteration) so unless you're doing a lot of iteration, the enhanced-for-loop approach is still perfectly valid.

    This sort of optimisation is micro-optimisation, and if you concentrate on this rather than looking at the big picture of your code, you're taking a self-defeating approach. By far the biggest optimisation you can make is to ensure your data structures correctly match your problem space. When you've done that, everything else has a tendency to fall into place.

    Now, there may be a tiny performance difference here (invokevirtual can be faster than invokeinterface in some circumstances) but again, we're talking about the sort of micro-optimisation that's self-defeating. You should always work with the most abstract type you can - it's good programming practice, and much better for code reusability. In any case, your benchmark isn't really meaningful - apart from the fact that milliseconds are way too coarse-grained for this, I can see plenty of places where the optimiser's going to trip you up there.

    Always code to interfaces, not implementations, where possible. Worthwhile reading: http://stackoverflow.com/questions/383947/what-does-it-mean-to-program-to-an-interface

    EDIT by Moderator: merged posts, please use the edit button instead of double posting.
     
    Last edited by a moderator: May 26, 2016
  18. Offline

    md_5

    <lazy>
    I recommend proguard, the worlds #1 Java optimizer!
    </lazy>
     
    Icyene likes this.
  19. Offline

    Dark_Balor

    By the way, only the name doesn't change between deconnection and reconnection. Then using the entity ID is useless (as keeping the player object of course).

    And yes you can store player object, if you paid attention to the quit event :)
     
  20. Offline

    ZachBora

    It's an obfuscator... why would we use this :(
     
  21. 1) Using the entity ID is fine if you don't need persistance
    2) Yes, but I prefer not to because if something happens where my plugin get's an error or freeze's there's a potential for memory leaks.
    All and all it's easier for new developers to just store the name and have a utility conversion methods rather than writing event checks
     
  22. Offline

    md_5

    It's actually also marketed as an Optimizer, just add -dontobfuscate.
    It does some nice things, such as inlining, optimisation and reduces the code size a fair bit as well.
     
  23. Offline

    ZachBora

    hmmm... inlining seems bad from what I just saw on the wiki about it.
     
  24. Offline

    Technius

    This thread has seen a lot of replies since I last checked... xD
     
  25. Offline

    Icyene

    Thanks for that! I find its very useful!

    P.S. I'm your 660th like! (yay!)
     
Thread Status:
Not open for further replies.

Share This Page