Java-Gaming.org Hi !
Featured games (83)
games approved by the League of Dukes
Games in Showcase (539)
Games in Android Showcase (133)
games submitted by our members
Games in WIP (603)
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  
  Avoiding creation of Color object  (Read 3149 times)
0 Members and 1 Guest are viewing this topic.
Offline LeCastor

Senior Newbie




Voxels will rule the world


« Posted 2004-04-21 09:44:32 »

Hi,

I have a small Applet, where I draw some stuff using g.drawLine(...) (g is a java.awt.Graphic object). I have to draw huge amounts of line, and depending of their position, a Color is chosen -> Everytime I draw a Line, a g.setColor(...) is called, with the desired Color.

First problem : As I have about 40 000+ lines, about 40 000 (!) Colors are created at every repaint.

What I want to do, is to create a single Color Object, and then change it's RGB values, avoiding a Constructor call. Example :
1  
2  
3  
4  
c.red = something;
c.green = something;
c.blue = something;
g.setColor(c);

or
1  
2  
c.setRGB(something);
c.setColor(c);


This would save me a lot of memory space, and will be faster (Object creation is too slow in this case, compared to simple field assignation).

Second problem : the java.awt.Color class does not have methods like setRed(int), setGreen, setBlue, there are no r, g, and b public fields.

I then wanted to subclass Color, in order to implement some methods to change the color value.

Third problem : The color value is stored in a private int field, which means that I can't change it outside of Color... I can't subclass Color, in order to implement my own setRGB method. The only way to change the color Value in a Color object is to recreat a Color object.

My question : Am I right ? Is there no way to change a color value without creating a new Color object ? Is there a way to do what I want ?

Thanks !
Offline kevglass

« JGO Spiffy Duke »


Medals: 212
Projects: 24
Exp: 18 years


Coder, Trainee Pixel Artist, Game Reviewer


« Reply #1 - Posted 2004-04-21 09:53:34 »

I believe what you're saying is java.awt.Color is non-mutable, which is true. If there are only a small number of colours you actually use keep them an instance variables instead of local.

If there are loads of colours you could implement some sort of ColorCache object which lets you request a colour specified by its red green and blue components. If its not in the cache, its created, added to the cache and returned. If its already in the cache then you just return your copy (preventing the construction).

Kev

Offline swpalmer

JGO Coder


Exp: 12 years


Where's the Kaboom?


« Reply #2 - Posted 2004-04-21 11:49:04 »

Kev is correct. Color is an immutable object, like String or Integer.  You can't change it.  The idea being that "red" is "red" and "red" cannot become "blue".  You can change the colour of something to a DIFFERENT colour, but the same colour object must always be a specific colour.  E.g. the Color.RED static member of the Color class would be useless if you could just redefine it to come out green instead.

More importantly, have you profiled your code to determine that it actually is the construction of Color objects that is the bottleneck? If not, do that first.  If it is the bottleneck then maybe try an object cache as kev suggests.

Games published by our own members! Check 'em out!
Legends of Yore - The Casual Retro Roguelike
Offline oNyx

JGO Coder


Medals: 2


pixels! :x


« Reply #3 - Posted 2004-04-21 12:23:56 »

For quick testing... just create one Color object and use it all the time. See if it makes a difference.

If so you could try 12bit colors - 4096 colors in total. Getting those ramps is rather easy:

1  
2  
3  
4  
5  
6  
7  
8  
9  
10  
for(int r=0;r<16;r++)
  {
   for(int g=0;g<16;g++)
   {
    for(int b=0;b<16;b++)
    {
     there you go -> new Color(r*17,g*17,b*17);
    }
   }
  }

弾幕 ☆ @mahonnaiseblog
Offline LeCastor

Senior Newbie




Voxels will rule the world


« Reply #4 - Posted 2004-04-21 12:54:31 »

Quote

More importantly, have you profiled your code to determine that it actually is the construction of Color objects that is the bottleneck? If not, do that first.  If it is the bottleneck then maybe try an object cache as kev suggests.


I've checked my code using JProfiler. The construction of the Color Objects is the Bottleneck of my drawing method. I've optimized the whole method (some calculation is performed), and the Color constructor is the last thing which cause a performance decrease.

I've tried to put a static color, as you say, and I've got a 20-30% decrease in execution time. Moreover, all of these Colors eat my memory : at certain moment (if JProfiler says right), up to 10Mb of RAM is chewed up by Color objects...

As all of you say, I think I'm gonna create a color table with pre-calculated colors, although it don't please me (that's a lot of objects storing...)  Smiley
Offline DoronBarak

Senior Newbie




Jave Games Rock, especially paper-rock-scissors!


« Reply #5 - Posted 2004-04-21 13:16:41 »

Try the following code:
1  
2  
3  
4  
5  
6  
7  
8  
9  
10  
11  
12  
13  
import java.lang.reflect.*;

      public static void setColor(Color colour, int rgb) {
            try {
                  Field f = colour.getClass().getDeclaredField("value");
                  f.setAccessible(true);
                  f.setInt(colour, rgb);
            } catch (IllegalAccessException ex) {
            } catch (IllegalArgumentException ex) {
            } catch (SecurityException ex) {
            } catch (NoSuchFieldException ex) {
            }
      }


Since it uses Reflection, you may have to sign your Applet, not sure...

Better yet, would be to cache the Color object, and the "value" declared field, then all you need to do is change the value of the cached field and re-use the color object.

Hope this helps,
Doron

Offline kevglass

« JGO Spiffy Duke »


Medals: 212
Projects: 24
Exp: 18 years


Coder, Trainee Pixel Artist, Game Reviewer


« Reply #6 - Posted 2004-04-21 13:19:40 »

You should profile it, but using reflection is more than likely going to be slower than just creating the appropriate objects.

It really is a inventive/hideous thing to do though. Wink

Kev

Offline LeCastor

Senior Newbie




Voxels will rule the world


« Reply #7 - Posted 2004-04-21 13:57:24 »

Quote
Try the following code:
1  
2  
3  
import java.lang.reflect.*;

     [...]


Since it uses Reflection, you may have to sign your Applet, not sure...

Better yet, would be to cache the Color object, and the "value" declared field, then all you need to do is change the value of the cached field and re-use the color object.

Hope this helps,
Doron


Great ! I'll give a try to this.

And signing the Applet is not a problem, as it is already signed (need to access a data file located on a CD-ROM).
Offline trembovetski

Senior Devvie




If only I knew what I'm talking about!


« Reply #8 - Posted 2004-04-22 03:09:16 »

LeCastor, What java version are you using?

I find it unlikely that the creation of Color objects would be the bottleneck in your app in any recent vm.

It's a very lightweight object, and it's creation is very cheap for the vm - especially in comparison in rendering line - there's way more objects being created during that process.

To test this you don't even need a profiler: try this, instead of creating a new Color object every time, use something like

1  
2  
Color colors[] = {Color.red, Color.green, Color.blue };
int colorNum = 0;


and in your rendring loop use this instead of creating
a new color.

1  
setColor(colors[colorNum++ % colors.length);


(the use of several colors instead of just one is to
emulate the pipeline revalidation when the color is changed, otherwise it won't be  a fair comparison)


Offline DoronBarak

Senior Newbie




Jave Games Rock, especially paper-rock-scissors!


« Reply #9 - Posted 2004-04-25 01:38:40 »

Quote
You should profile it, but using reflection is more than likely going to be slower than just creating the appropriate objects.

It really is a inventive/hideous thing to do though. Wink

Kev

Profiling ANYTHING you create is a good idea before production stage, but even though Reflection has a bad name for being "slow" it still may surprise you.

Consider what would happen if a method using Reflection is JITed, wouldn't that provide almost the same speed as "natively" performing the same action through non-Reflection code?

Games published by our own members! Check 'em out!
Legends of Yore - The Casual Retro Roguelike
Offline trembovetski

Senior Devvie




If only I knew what I'm talking about!


« Reply #10 - Posted 2004-04-25 03:39:51 »

But it's not a question of being jitted or not, the operation itself can be much more costly - like, it could take some global lock. No jitting would help that.

Also, believe me, you don't want to change the values of already created Color object. If it was built to be immutable, you don't know what assumptions our implementation makes, you may run into some tricky issues.

Offline DoronBarak

Senior Newbie




Jave Games Rock, especially paper-rock-scissors!


« Reply #11 - Posted 2004-04-25 17:30:36 »

Quote
But it's not a question of being jitted or not, the operation itself can be much more costly - like, it could take some global lock. No jitting would help that.

Also, believe me, you don't want to change the values of already created Color object. If it was built to be immutable, you don't know what assumptions our implementation makes, you may run into some tricky issues.



I'm not sure, but I don't think that Reflection is built the way you describe it, this probably necessitates a quick look in Sun's sources? Wink

Also, sometimes, there's just no choice, believe me when I say that library-developers don't always come up with the max usage of their libraries.  Sometimes, you have to inherit something just to expose parts that have been left private cause that's what your Client actually needs. (Last sentence wasn't from the perspective of a game programmer)

Offline kevglass

« JGO Spiffy Duke »


Medals: 212
Projects: 24
Exp: 18 years


Coder, Trainee Pixel Artist, Game Reviewer


« Reply #12 - Posted 2004-04-25 17:32:38 »

True enough, but any time you do that you're taking the risk the library developer has made assumptions based on their protection of member variables.

Using reflection to get at um is just evil tho Wink

Kev

Offline trembovetski

Senior Devvie




If only I knew what I'm talking about!


« Reply #13 - Posted 2004-04-26 01:39:15 »

Actually, in the case of Color object I can guarantee that you'll break a lot of stuff by making it mutable, and thus breaking the contract. Our code relies on the fact that it's immutable, so there are places where we cache the currently set color, and check if the new color is the same object as the old one, and do not modify some structures if it is.

BTW, I'm still not convinced that it's the creation of the Color object is the bottleneck in your app..
Offline LeCastor

Senior Newbie




Voxels will rule the world


« Reply #14 - Posted 2004-04-26 14:57:30 »

Quote

BTW, I'm still not convinced that it's the creation of the Color object is the bottleneck in your app..


I know Wink. Actually, it is the last time-consuming operation in my drawing routine. Every other operations consist of simple calculation. The problem of creation of Color object, is that it creates too much objects in memory, as many are created within a small amount of time (which means if don't get garbage collected immediately, and when it has to be, zillions of objects are collected).

I had the small problem with another program. It was a 6-DOF 3D camera system, using Quaternion. The program was slow. I profiled it, and realised that 200 000+ Quaternions were created each 5 secondes  Shocked... I changed the way i was doing my operation, with just one Quaternion created at camera creation, re-using the same one everytime. Result, 50% + speed increase. Believe me or not.

Same goes here. Lots of lines are drawed in a small amount of time. Which means lots of time in spent in Color object creation, memory allocation, field assignation, etc... It is still slower than just change the value itself.

I agree, a Color object is immutable. But I don't really care about programming philosophy here. All I want is to store a Color value somewhere, without having to recreate an object everytime. All of you know (or should know Wink ) it's bad to create 20000 objects per second.
Offline swpalmer

JGO Coder


Exp: 12 years


Where's the Kaboom?


« Reply #15 - Posted 2004-04-26 15:22:33 »

Could you post some example code that illustrates the problem?  Maybe we can find alternatives or at least better understand what is happening.

Offline DoronBarak

Senior Newbie




Jave Games Rock, especially paper-rock-scissors!


« Reply #16 - Posted 2004-04-27 12:30:15 »

Quote
True enough, but any time you do that you're taking the risk the library developer has made assumptions based on their protection of member variables.

Using reflection to get at um is just evil tho Wink

Kev


It may be evil, but so is hiding stuff you absolutely have to expose cause there's no other way to accomplish a desired functionality.  I've had the same problem with some classes in the Delphi Component Libraries once, there was no choice but to subclass and re-expose things that were hidden.

Also, it has occured to me that caching the Color object and its "value" Field may speed things up a notch cause you wouldn't be searching for the Field and setting its access flag every time you'd want to change the colour-value! Smiley

Another thing, where would Java's Serialization, RMI and plug&play Java-Bean-Box model be without Reflection? Wink

Offline DoronBarak

Senior Newbie




Jave Games Rock, especially paper-rock-scissors!


« Reply #17 - Posted 2004-04-27 12:50:00 »

Quote
All of you know (or should know ;) ) it's bad to create 20000 objects per second.


I think this code is better:

1  
2  
3  
4  
5  
6  
7  
8  
9  
10  
11  
12  
13  
14  
15  
16  
17  
18  
19  
20  
21  
22  
23  
24  
25  
26  
27  
28  
29  
30  
31  
32  
33  
34  
35  
36  
37  
38  
39  
40  
41  
42  
43  
44  
45  
46  
47  
48  
49  
50  
51  
52  
53  
54  
55  
56  
57  
58  
59  
import java.awt.Color;
import java.awt.color.ColorSpace;
import java.lang.reflect.*;

public class Colour extends Color {
      private Field cachedColourValueField;

      private void cacheColorField() {
            try {
                  cachedColourValueField = getClass().getDeclaredField("value");
                  cachedColourValueField.setAccessible(true);
            } catch (NoSuchFieldException ex) {
            }
      }

      public void setRGB(int rgb) {
            if (cachedColourValueField != null) {
                  try {
                        cachedColourValueField.setInt(this, rgb);
                  } catch (IllegalAccessException ex) {
                  }
            }
      }

      public Colour(int r, int g, int b) {
            super(r, g, b);
            cacheColorField();
      }

      public Colour(int r, int g, int b, int a) {
            super(r, g, b, a);
            cacheColorField();
      }

      public Colour(int rgb) {
            super(rgb);
            cacheColorField();
      }

      public Colour(int rgba, boolean hasalpha) {
            super(rgba, hasalpha);
            cacheColorField();
      }

      public Colour(float r, float g, float b) {
            super(r, g, b);
            cacheColorField();
      }

      public Colour(float r, float g, float b, float a) {
            super(r, g, b, a);
            cacheColorField();
      }

      public Colour(ColorSpace cspace, float[] components, float alpha) {
            super(cspace, components, alpha);
            cacheColorField();
      }
}


This is sort of a compromise which keeps the "dirty-work" done in the appropriate class "Colour", letting you have the setRGB(..) method and still claim you're using a proper Color object ;)

I would also like to add -- and it may seem evil to some -- that the only time you're actually using a Color object is when you're painting with it, and then, and only then would its RGB value have to be unmutable, isn't that right? :)

Offline princec

« JGO Spiffy Duke »


Medals: 435
Projects: 3
Exp: 16 years


Eh? Who? What? ... Me?


« Reply #18 - Posted 2004-04-27 13:11:13 »

I find it very, very hard to believe there was a good reason for making Color immutable, and harder to believe still that anyone could think that creating millions of the buggers wasn't going to cause a major performance drain in a rendering loop even with a garbage collector written by God himself and all his little wizards.

Cas Smiley

Offline swpalmer

JGO Coder


Exp: 12 years


Where's the Kaboom?


« Reply #19 - Posted 2004-04-27 18:41:28 »

Seems reasonable to me.  Not that I don't see that there are cases for not having it immutable.  But immutable objects allow for optimizations just as mutable ones do.  

Now in the specific case of a Color object.. yeah maybe you are right, since ultimately the optimizations that Trembovetski speaks of could be a simple compare or the 32 bit Color ref, or the 32 bit value of the RGB data.  In fact in the latter the code would be optimized to not trigger changes for *different* Color objects that actually referred to the *same* colour values.  The optimizations could be written to handle both cases.  First check the Color ref, if it is different check the value to be sure reseting the graphics pipeline is going to be worth it.

I guess it is just the cost of an extra pointer dereference to get at the value.  unless you totally throw out type safety and use 'int' for colors instead.

Offline trembovetski

Senior Devvie




If only I knew what I'm talking about!


« Reply #20 - Posted 2004-04-28 03:19:16 »

I'm not discussing the desing decisions which made Color immutable, I'm just saying that making it mutable will break things in our implementation.

Here's one example of some code we have in
Graphics2D.setColor(Color newColorObject)
1  
2  
3  
4  
if (newColorObject == alreadySetColor)
  return;
alreadySetColor = newColorObject;
rgbValue= newColorObject.getRgb();

and then later this int rgb value is used all over.
So, if you modify the Color object and try to set it again, it'll be rejected since we'll thing it's aready set. Also, since rgbValue is cached, the graphics context won't notice that you've changed the color object's value.

This is just one example which is easy to work around once you know about it, but I wouldn't dare to say it'll be the last one. Needless to point, that any such workarounds would not be guaranteed to work across releases or different vm versions.

And, I'll repeat, I'm _still_ not convinced that the creation of Color objects is a problem with your app as I haven't seen any proof yet. =)
Offline LeCastor

Senior Newbie




Voxels will rule the world


« Reply #21 - Posted 2004-04-28 07:48:39 »

Want some proof Wink ?

I've just made two tests. One with Color creation, one with color caching.

Here are the result (drawing time displayed, and -verbosegc in jvm option)

With Color caching :

Draw Time : 290
Draw Time : 270
Draw Time : 270
Draw Time : 290
Draw Time : 271
Draw Time : 280
Draw Time : 280
Draw Time : 250
Draw Time : 281
Draw Time : 251

Min : 250; Max : 290

With "Dynamic" Color :

[GC 2275K->1635K(9736K), 0.0018497 secs]
[GC 2275K->1635K(9736K), 0.0012935 secs]
[GC 2275K->1635K(9736K), 0.0013415 secs]
Draw Time : 330
[GC 2275K->1635K(9736K), 0.0015390 secs]
[GC 2275K->1635K(9736K), 0.0012982 secs]
[GC 2275K->1635K(9736K), 0.0012909 secs]
[GC 2275K->1635K(9736K), 0.0015237 secs]
Draw Time : 311
[GC 2275K->1635K(9736K), 0.0013256 secs]
[GC 2275K->1635K(9736K), 0.0014139 secs]
[GC 2275K->1635K(9736K), 0.0021162 secs]
Draw Time : 320
[GC 2275K->1635K(9736K), 0.0013516 secs]
[GC 2275K->1635K(9736K), 0.0013242 secs]
[GC 2275K->1635K(9736K), 0.0013158 secs]
Draw Time : 321
[GC 2275K->1635K(9736K), 0.0014820 secs]
[GC 2275K->1635K(9736K), 0.0013382 secs]
[GC 2275K->1635K(9736K), 0.0014234 secs]
[GC 2275K->1635K(9736K), 0.0013158 secs]
Draw Time : 310
[GC 2275K->1635K(9736K), 0.0013270 secs]
[GC 2275K->1635K(9736K), 0.0019173 secs]
[GC 2275K->1635K(9736K), 0.0017782 secs]
Draw Time : 320
[GC 2275K->1635K(9736K), 0.0014256 secs]
[GC 2275K->1635K(9736K), 0.0014493 secs]
[GC 2275K->1635K(9736K), 0.0013700 secs]
Draw Time : 301
[GC 2275K->1635K(9736K), 0.0014474 secs]
[GC 2275K->1635K(9736K), 0.0013697 secs]
[GC 2275K->1635K(9736K), 0.0013683 secs]
[GC 2275K->1635K(9736K), 0.0013147 secs]
Draw Time : 301
[GC 2275K->1635K(9736K), 0.0013929 secs]
[GC 2275K->1635K(9736K), 0.0013558 secs]
[GC 2275K->1635K(9736K), 0.0011873 secs]
Draw Time : 300
[GC 2275K->1635K(9736K), 0.0010923 secs]
[GC 2275K->1635K(9736K), 0.0013901 secs]
[GC 2275K->1635K(9736K), 0.0011163 secs]
Draw Time : 320

Min : 300; Max : 330

Best difference : 80 ms. Worst difference : 10 ms. Moreover, garbage collection in the second case.

Notice that in the second case, additionnal calculus is performed (as it has to determine the color value).

I can precompute the calculus though. But if I precompute the calculus, I can also directly precompute the Colors (a 64-only precomputed-Colors array is ok).

-> I'm gonna implement a ColorCache class which holds a precomputed-Colors array, just as you guys said at the beginning of the thread. I think it's the best solution.

DoronBarak, your solution (Colour class with cacheColorField) is great, and I may use it for other programs.

Thanks to all ! (discussion may continue if you want)
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.

rwatson462 (37 views)
2014-12-15 09:26:44

Mr.CodeIt (31 views)
2014-12-14 19:50:38

BurntPizza (62 views)
2014-12-09 22:41:13

BurntPizza (99 views)
2014-12-08 04:46:31

JscottyBieshaar (60 views)
2014-12-05 12:39:02

SHC (74 views)
2014-12-03 16:27:13

CopyableCougar4 (77 views)
2014-11-29 21:32:03

toopeicgaming1999 (138 views)
2014-11-26 15:22:04

toopeicgaming1999 (127 views)
2014-11-26 15:20:36

toopeicgaming1999 (38 views)
2014-11-26 15:20:08
Resources for WIP games
by kpars
2014-12-18 10:26:14

Understanding relations between setOrigin, setScale and setPosition in libGdx
by mbabuskov
2014-10-09 22:35:00

Definite guide to supporting multiple device resolutions on Android (2014)
by mbabuskov
2014-10-02 22:36:02

List of Learning Resources
by Longor1996
2014-08-16 10:40:00

List of Learning Resources
by SilverTiger
2014-08-05 19:33:27

Resources for WIP games
by CogWheelz
2014-08-01 16:20:17

Resources for WIP games
by CogWheelz
2014-08-01 16:19:50

List of Learning Resources
by SilverTiger
2014-07-31 16:29:50
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!