Java-Gaming.org    
Featured games (79)
games approved by the League of Dukes
Games in Showcase (477)
Games in Android Showcase (107)
games submitted by our members
Games in WIP (535)
games currently in development
News: Read the Java Gaming Resources, or peek at the official Java tutorials
 
    Home     Help   Search   Login   Register   
Pages: [1]
  ignore  |  Print  
  4k Code Optimization Tips list  (Read 2560 times)
0 Members and 1 Guest are viewing this topic.
Offline Damocles
« Posted 2011-11-25 15:09:22 »

Hi, lets enter here some Optimization Tips for 4k as a quick overview:

2 must-sees first:
http://www.java4k.com/index.php?action=view&page=getstarted (Java4k Homepage links)
http://www.indiespot.net/app/java-four-kay (Compile&Shrink)

-----

Here some findings:

Code that looks "Optimized" and short often does not result in a smaller jar/pack200.

one example:
using
Version1:
Color c1=new Color(200,100,0);
looks worse than
Version2:
Color c1=new Color(0xc86400);

but when defining many colors
the first version is actually better when the color-parameters repeat,

Color c1=new Color(200,100,0);
Color c2=new Color(100,200,0); //good
(but: Color c2=new Color(102,203,0); //not good)

I think the reason is, that while the first version writes a shorter bytecode, the
second version is easier for the compressor to find a pattern in. So contracting information
into less bytes already in the sourcecode can render the efficiency of the packer useless.

another example:

to store a bytearray inside a string, where each value was between 1 and 10
I tried to compress it into UTF characters
"\u24fe\u21ee" -> storing 4 entries per character, eg 2 entries per byte
where my earlier version used a primitive use of simple character for each entry
"ABCABAAC" -> storing 1 entry per byte

In the end the second version compressed much better.
I think because the packer uses only "full" bytes to build its dictionary.

----

A quick way to test changes is to use an Hex Editor for example, to see the size of the classfile,
and compare it on the byte-level if any fancy code-constructions are not resulting in the same output as the "primitive" version.

----

a > b  is not shorter than a >= b .... so dont worry using >= <= etc. in comparissons/loops when needed

----

you can use a decompiler to see your own results, after obfuscation.
This really helps in some cases to see what was obfuscated away, and does not need improvement in
the source.


-----

a last resort can always be to to "switch" positions of codelines, where the order does not matter.
The packer might find a better pattern in the new version.
This could be automated.
(only usefull at the very final packing up of the game, small changes can render the result useless)

------

more:

Offline moogie

JGO Knight


Medals: 12
Projects: 6
Exp: 10 years


Java games rock!


« Reply #1 - Posted 2011-11-26 09:58:09 »

some general optimisations:

- a do while loop uses less byte code than just a plain while loop

- try to use numerics constants between -127 and +127 as these will be stored as byte type in contants bool

- when looping or in any situation you can manage, perform the conditional test against 0, 1 or -1 as these have special short byte code representaions. i.e.
1  
for (i=9;i>=0;--i)


instead of

1  
for (i=0;i<10;++i)


- whilst it used to be true, i am not so sure any more with the latest compilers, using a pre-increment (--i) generated smaller byte code than post-increment (i++) but i guess it cant hurt to try.
Offline Morre

JGO Knight


Medals: 2
Projects: 10


I'm Dragonene on IRC.


« Reply #2 - Posted 2011-11-27 23:30:14 »

I'd like to point out that while some of the above tips are undoubtedly true and useful for squeezing out that extra byte, they're not the sort of optimizations that'll make or break a 4k game.

The basics, as suggested on the 4k games wiki (there's a link on java4k.com, or you can go here) are what one should consider first. No custom classes, no methods beyond the main method, careful handling of input and rendering... these will go a long way towards helping beginners. I'd strongly recommend having a look at one of the many 4k applet templates posted in these forums this year and earlier years.

As for tiny optimizations, somebody once pointed out that instead of using float arrays to represent objects, one might use classes already present in the standard API - for example, an instance of Arc2D.Double can hold 6 doubles for you, or Rectangle could hold four. Now whether this is an optimization or not is a bit uncertain... I've seen it generate both smaller and larger files after compression, but it might be worth a shot for saving a few extra bytes if you're in the final stages of optimization.

EDIT: Oh, and just because it doesn't seem to be mentioned on the wiki, keeping your class name short (preferably one letter only) saves some space, as obfuscators won't normally rename your class. I don't know whether Riven's tool does this for you, though.

EDIT2 : I noted that the java.net main wiki is being shut down, and the java 4k games design wiki with it. I've requested it be exported as per the instructions on the site, so we'll see how that goes.

Games published by our own members! Check 'em out!
Legends of Yore - The Casual Retro Roguelike
Offline Damocles
« Reply #3 - Posted 2011-11-28 06:27:46 »

True its not a starter guide I ment here.
More of a collection of specific tips/findings.
For beginners, the framework thread last year is very helpful.

http://www.java-gaming.org/topics/java-4k-resources-thread/21630/view.html

Here: example templates
http://www.java-gaming.org/topics/applet-templates/21626/view.html

Offline Morre

JGO Knight


Medals: 2
Projects: 10


I'm Dragonene on IRC.


« Reply #4 - Posted 2011-11-28 09:23:38 »

Yep, I understood as much, just wanted to make it clear to anybody who stumbles over this thread.

With that said, I'm sure any additional insight is appreciated. Smiley

Offline Gudradain
« Reply #5 - Posted 2011-11-29 21:10:33 »

I guess I will explain the beginner technique I used for making my game.

My problem was that I don't know how to organize my code without class. Consider the following :

1  
2  
3  
4  
5  
6  
7  
8  
9  
10  
11  
12  
13  
14  
public class Map{

ArrayList<Unit> unitList;

}

public class Unit{

value1;
value2;
value3;
...

}


This kind of scenario happens very often. You have a list of object each with their own attribute so it makes sense to make another class for them. But in Java4k competition making multiple class is a no no.

So here is a simple solution to that problem.

1  
2  
3  
4  
5  
6  
7  
8  
9  
10  
public class Map{

ArrayList<Map> unitList;

unitValue1;
unitValue2;
unitValue3;
...

}


Now your map class can act both as the Map class and the Unit class. And it makes your code much smaller to have only one class. I find it easier to write my game in multiple class and then shrinking it's size using that technique.
Offline Groboclown
« Reply #6 - Posted 2011-11-29 22:11:20 »

My problem was that I don't know how to organize my code without class.

For a 4k game, you want to avoid using the OO way of insulating data with code, and trim down the problem to just the data.  In your example, you can simulate a double array (int[][]) by having a fixed amount of data stored per unit (UNIT_DATA_SIZE), and indexing the data per unit:
1  
2  
3  
4  
5  
6  
7  
8  
9  
10  
11  
12  
13  
14  
private static final int MAX_UNIT_COUNT = 30;
private static final int UNIT_DATA_SIZE = 5;
private static final int UNIT_POS_X_INDEX = 0;
private static final int UNIT_POS_Y_INDEX = 1;
private static final int UNIT_VEL_X_INDEX = 2;
private static final int UNIT_VEL_Y_INDEX = 3;
private static final int UNIT_HEALTH_INDEX = 4;

...
int[] units = new int[MAX_UNIT_COUNT * UNIT_DATA_SIZE];

int unit = 3;
int unitx = units[unit * UNIT_DATA_SIZE + UNIT_POS_X_INDEX];
int unity = units[unit * UNIT_DATA_SIZE + UNIT_POS_Y_INDEX];

and so on.  Further optimizations can be made by changing the referencing of the array index by using shifts (change UNIT_DATA_SIZE to UNIT_DATA_SIZE_SHL, and have "(unit << UNIT_DATA_SIZE_SHL)" ).  It's a bit inefficient for memory usage, but makes smaller bytecode.

Offline Damocles
« Reply #7 - Posted 2011-11-29 22:16:31 »

I alway start of using methods and arrays (sometime classes)
Just to quickly get the prototype working.

Only in the later steps I contract it down. (copy-past the methods in, contract everything into one array etc)

Optimizing the code too early can leave you with coding on a messi basis.

Its not too hard to get a working code smaller. At least easier than working on
a preoptimized and hard to read sourcecode.

Offline Gudradain
« Reply #8 - Posted 2011-11-30 06:50:25 »

My problem was that I don't know how to organize my code without class.

For a 4k game, you want to avoid using the OO way of insulating data with code, and trim down the problem to just the data.  In your example, you can simulate a double array (int[][]) by having a fixed amount of data stored per unit (UNIT_DATA_SIZE), and indexing the data per unit:
1  
2  
3  
4  
5  
6  
7  
8  
9  
10  
11  
12  
13  
14  
private static final int MAX_UNIT_COUNT = 30;
private static final int UNIT_DATA_SIZE = 5;
private static final int UNIT_POS_X_INDEX = 0;
private static final int UNIT_POS_Y_INDEX = 1;
private static final int UNIT_VEL_X_INDEX = 2;
private static final int UNIT_VEL_Y_INDEX = 3;
private static final int UNIT_HEALTH_INDEX = 4;

...
int[] units = new int[MAX_UNIT_COUNT * UNIT_DATA_SIZE];

int unit = 3;
int unitx = units[unit * UNIT_DATA_SIZE + UNIT_POS_X_INDEX];
int unity = units[unit * UNIT_DATA_SIZE + UNIT_POS_Y_INDEX];

and so on.  Further optimizations can be made by changing the referencing of the array index by using shifts (change UNIT_DATA_SIZE to UNIT_DATA_SIZE_SHL, and have "(unit << UNIT_DATA_SIZE_SHL)" ).  It's a bit inefficient for memory usage, but makes smaller bytecode.

Yeah but if not all your data are int you can't do that. My method is more flexible.
Offline Gudradain
« Reply #9 - Posted 2011-11-30 06:55:09 »

Also, I just tried your method and my method to compare the byte difference. The result might surprised you.

1  
2  
3  
4  
5  
6  
7  
8  
public class Test1 {
   
   int value1;
   int value2;
   
   Test1 [] unit;

}


1  
2  
3  
4  
5  
6  
7  
8  
9  
public class Test2 {
   
   public static final int unitSize = 2;
   public static final int unitValue1Index = 0;
   public static final int unitValue2Index = 1;
   
   int [] unit;

}


Test1 : 328 bytes
Test2 : 406 bytes

So, in a very simple example, my method seems better.

Prove me that I'm wrong pls.  Smiley

EDIT : I changed all the variable name so they become 1 character long to make sure that it wasn't that that make the difference and the result are still in my favor :

Test1 : 318 bytes
Test2 : 371 bytes
Games published by our own members! Check 'em out!
Legends of Yore - The Casual Retro Roguelike
Offline Bonbon-Chan

JGO Coder


Medals: 12



« Reply #10 - Posted 2011-11-30 10:54:06 »

My problem was that I don't know how to organize my code without class.

For a 4k game, you want to avoid using the OO way of insulating data with code, and trim down the problem to just the data.  In your example, you can simulate a double array (int[][]) by having a fixed amount of data stored per unit (UNIT_DATA_SIZE), and indexing the data per unit:
1  
2  
3  
4  
5  
6  
7  
8  
9  
10  
11  
12  
13  
14  
private static final int MAX_UNIT_COUNT = 30;
private static final int UNIT_DATA_SIZE = 5;
private static final int UNIT_POS_X_INDEX = 0;
private static final int UNIT_POS_Y_INDEX = 1;
private static final int UNIT_VEL_X_INDEX = 2;
private static final int UNIT_VEL_Y_INDEX = 3;
private static final int UNIT_HEALTH_INDEX = 4;

...
int[] units = new int[MAX_UNIT_COUNT * UNIT_DATA_SIZE];

int unit = 3;
int unitx = units[unit * UNIT_DATA_SIZE + UNIT_POS_X_INDEX];
int unity = units[unit * UNIT_DATA_SIZE + UNIT_POS_Y_INDEX];

and so on.  Further optimizations can be made by changing the referencing of the array index by using shifts (change UNIT_DATA_SIZE to UNIT_DATA_SIZE_SHL, and have "(unit << UNIT_DATA_SIZE_SHL)" ).  It's a bit inefficient for memory usage, but makes smaller bytecode.

1  
2  
3  
4  
5  
6  
7  
8  
9  
10  
11  
12  
13  
14  
private static final int MAX_UNIT_COUNT = 30;
private static final int UNIT_DATA_SIZE = 5;
private static final int UNIT_POS_X_INDEX  = 0*MAX_UNIT_COUNT;
private static final int UNIT_POS_Y_INDEX  = 1*MAX_UNIT_COUNT;
private static final int UNIT_VEL_X_INDEX  = 2*MAX_UNIT_COUNT;
private static final int UNIT_VEL_Y_INDEX  = 3*MAX_UNIT_COUNT;
private static final int UNIT_HEALTH_INDEX = 4*MAX_UNIT_COUNT;

...
int[] units = new int[MAX_UNIT_COUNT * UNIT_DATA_SIZE];

int unit = 3;
int unitx = units[unit + UNIT_POS_X_INDEX];
int unity = units[unit + UNIT_POS_Y_INDEX];


It's better this way, no ?

Nevertheless, i have compact every tables in 2 table (one of ints and one of doubles). I set table to the same size and same steps (better compression).
Result : 4 131 --> 4 028  Grin I'm happy

Offline Groboclown
« Reply #11 - Posted 2011-11-30 21:26:59 »

Also, I just tried your method and my method to compare the byte difference. The result might surprised you.
It doesn't surprise me.  Running javap -c -private on each class, you'll see that the static variables are still in the class file.  Let's do some small changes, in order to better reflect actual usage*:
1  
2  
3  
4  
5  
6  
7  
8  
9  
10  
11  
12  
13  
14  
15  
16  
17  
18  
19  
public class Test1 implements Runnable {
        private int value1;
        private int value2;
        private Test1[] unit;
        public void run() {
                unit = new Test1[1];
                System.out.println(unit[0]);
        }
}
public class Test2 implements Runnable {
        private static final int UNIT_SIZE = 2;
        private static final int UNIT_VALUE1_INDEX = 0;
        private static final int UNIT_VALUE2_INDEX = 1;
        private int[] unit;
        public void run() {
                unit = new int[1];
                System.out.println(unit[UNIT_VALUE1_INDEX]);
        }
}


Now let's run the classes through a minimizer (I picked Proguard for this test). I see this:

Test1.class (javac output): 562 bytes
Test1.class (proguard output): 363 bytes
Test2.class (javac output): 635 bytes
Test2.class (proguard output): 339 bytes

That means, with proper optimization, the array size difference is 24 bytes smaller**.  And that's for a trivial example.  Once you start getting into accessing members, rather than just an index, the difference becomes much greater.

(Part of this magic comes from the minimizer stripping out those constant declarations and uses them inline where needed.)

* I added the "run" method to ensure the variable is not stripped out of the class due to being not used.  I added "implements Runnable" to ensure the "run" method isn't removed.
** There is a slight difference with the signature invoked between these two run methods: one invokes java/io/PrintStream.println:(Ljava/lang/Object;)V, the other invokes java/io/PrintStream.println:(I)V, a difference of 17 bytes, but still that makes Test1.class 7 bytes bigger)

Offline SimonH
« Reply #12 - Posted 2011-12-01 00:46:31 »

magic comes from the minimizer stripping out those constant declarations and uses them inline where needed
Absolutely. If you want uber-small code then base your game around 1 or 2 big arrays. Works pretty good for me!

People make games and games make people
Offline Gudradain
« Reply #13 - Posted 2011-12-01 06:37:33 »

Wow it's really magic Smiley. Thx for the explanation
Offline SquashMonster

Senior Member


Medals: 1
Projects: 2



« Reply #14 - Posted 2011-12-09 09:33:01 »

Might be the wrong place, but this is what I used in Burning Man to fit so many levels into a small space -

Assuming your game has tiles, and there are several different sorts of tiles, the naive way to store them is a big array that goes left to right, with a byte or part of a byte for each tile type in order.  You can improve this by adding things like back references (something like: tile type 15 doesn't exist, it means the next byte says how many bytes backwards to look, and the byte after that says how long to keep copying old bytes).  However, this comes out to much larger file sizes than necessary as well.

The way to really pack those down is what the ROM hacking scene calls an object format (or they did when I was a part, it's been ten years).  The idea here is that your level files consist of a series of records like Type, X, Y, Width, Height - bit-packed however works for your specific case, of course (do not bit pack across byte lines, the space savings in the file will almost always get canceled out by the extra code).  Write a pair of for loops that take those stats and fill in that tile type in your array, and do that for each entry in order.

So, if your game has a 16x16 grid, and you have 4 types of objects, your format could look something like this:

XXXXYYYY WWWHHHTT - one nibble of X, one nibble of Y, three bits each of width and height, and two bits for type

You also need a terminator that says when a level is done.  Pick a value you'll never use for the first byte and quit when you get there.  I normally use 0xFF because that's what everything ever uses in ROM hacking, but you don't have to do that.  In the above format, that would mean no objects can start in the lower right corner.

An example level with the above looks something like this:

00001000 11111101 //x=0, y=8, width=8, height=8, type = 1.  A rectangle of stone blocks in the lower left quadrant
10001010 11110101 //x=8, y=10, width=8, height=6, type = 1.  A lower rectangle of stone blocks in the lower right quadrant
11001010 01101111 //x=12, y=10, width=3, height=3, type = 3.  A small pool of water in the lower right quadrant
11111111 // FF terminator

Which is to say, roughly the first level of Burning Man.  That's 9 bytes.  (The real thing uses 13 bytes)

Writing this out by hand is AWFUL so don't.  I wrote a level editor for Burning Man.  Your editor doesn't need to be 4k at all so go nuts and do it the normal way.  Your editor doesn't need to be used by anyone else either, so go nuts and make it super unfriendly.  The Burning Man editor was write-only (I remade levels from scratch every time I wanted to modify one, this helped keep size down) and would only do one level.  I used cat to put the level set together.
Pages: [1]
  ignore  |  Print  
 
 
You cannot reply to this message, because it is very, very old.

 

Add your game by posting it in the WIP section,
or publish it in Showcase.

The first screenshot will be displayed as a thumbnail.

pw (37 views)
2014-07-24 01:59:36

Riven (38 views)
2014-07-23 21:16:32

Riven (26 views)
2014-07-23 21:07:15

Riven (28 views)
2014-07-23 20:56:16

ctomni231 (59 views)
2014-07-18 06:55:21

Zero Volt (50 views)
2014-07-17 23:47:54

danieldean (42 views)
2014-07-17 23:41:23

MustardPeter (44 views)
2014-07-16 23:30:00

Cero (60 views)
2014-07-16 00:42:17

Riven (57 views)
2014-07-14 18:02:53
HotSpot Options
by dleskov
2014-07-08 03:59:08

Java and Game Development Tutorials
by SwordsMiner
2014-06-14 00:58:24

Java and Game Development Tutorials
by SwordsMiner
2014-06-14 00:47:22

How do I start Java Game Development?
by ra4king
2014-05-17 11:13:37

HotSpot Options
by Roquen
2014-05-15 09:59:54

HotSpot Options
by Roquen
2014-05-06 15:03:10

Escape Analysis
by Roquen
2014-04-29 22:16:43

Experimental Toys
by Roquen
2014-04-28 13:24:22
java-gaming.org is not responsible for the content posted by its members, including references to external websites, and other references that may or may not have a relation with our primarily gaming and game production oriented community. inquiries and complaints can be sent via email to the info‑account of the company managing the website of java‑gaming.org
Powered by MySQL Powered by PHP Powered by SMF 1.1.18 | SMF © 2013, Simple Machines | Managed by Enhanced Four Valid XHTML 1.0! Valid CSS!