We are about to switch to a new forum software. Until then we have removed the registration on this forum.
Hi everyone, I found an old project of mine, that I'd like to get some criticism on. I can clearly see that the code is bad, but would like to know/see what you would have done instead of what I did. So please #RoastMyCode:
Android_Plot.pde http://pastebin.com/8NmxjRQn
Plot3D.pde http://pastebin.com/pWEuPk78
Comments
It is an interesting concept. I like to say your code is not constrained to android mode but could be run in Processing as well.
I think a better exercise is if you comment what you would do different and people could comment on those thoughts.
I added the code below so people can run it themselves.
Kf
Great improvement @kfrajer. It's easier to use code this way. (It goes without saying that the original code was also good, but the proper formatting and commenting added a new level of detail)
@Lord_of_the_Galaxy I didn't change the code, not a bit. I just copied it and pasted it here from the provided link so people can run it themselves (try to make it easier for other people to see it themselves). I didn't add any comments and I am more interested to learn what the OP has to say about its design.
Kf
@kfrajer The formatting has changed. That's what I meant. I guess that was automatic.
1. Lines like this should be shortened:
plot3D(int _xCenter, int _yCenter, int _vertices, int _rings, int _xBegin, int _xEnd, int _drawMethod, float _xRatio, float _yRatio) {
I wish you could parse a struct or something to the constructor. I'm not sure what the approach should be in Processing.2. I think that the plot3D should have different methods. Some for setting the class' variables etc. settingsChanged() and rotatePlot() should be private methods.
4. I'd like rotatePlot to not depend on variables 'public' inside the class plot3d if it's possible.
5. Comments should be homogeneous.
use
PVector
more often to bundle all the x,y lines that belong togetherplot3D should / could take a class as parameter
@Chrisir why put x,y and (,z) in a PVECTOR? The lines are calculated once when settingsChanged() and are only used by createShape(); stuff once. I don't see why putting the lines into a PVECTOR arraylist and then looping trough that arraylist for createShape makes sense?
I mean all of the code.
You can almost cut it by half in using PVectors.
Eg lines 18 to 26 in the 2nd post from the top and then nearly all variables following
Conceptually I also think PVector is clearer since you have in one class what belongs together
Hmm... Could you please elaborate, @Chrisir? I don't see how rotation vars etc. could be replaced by using the PCVECTOR class. PVECTOR::rotate exists, but only rotates 2D. Are you sure this is the appropriate thing to do thinking performance?
I am just talking about the storage of variables- just instead of centerx,centery use PVector center
center.x and center.y
@chrisir arh, I see. That would definitely be 'nicer'. But isn't it a bit of a waste to create and instances of the relatively big class just to store to tiny vars?
I don't know
When we use
new
(or some other hack) to instantiate any class or array, the actual contents of the object created are just made outta non-static
fields, plus a header. @-)All of the rest of the class' members stay in the class. I-)
All methods, constructors &
static
fields aren't instantiated at all! \m/However, Processing's PVector got an extra useless field called array[]: :-&
transient protected float[] array;
Even though it's forever
null
all the time, until we actually invoke method array(), it still wastes 4 bytes of RAM. X(Therefore, if we don't need PVector's methods, we're better of creating another class to have x, y & z fields only, w/o any useless crap fields! =P~
Cross-linking it to your previous comment @GoToLoop: https://forum.processing.org/two/discussion/comment/92271/#Comment_92271
Kf
@GoToLoop Some people don't respect the resources they've been given by Intel/AMD. I will of course create a class that can do just that!
PVector wouldn't take much space / processor time
as gotoloop wrote:
(by the way, "wastes 4 bytes of RAM" - four bytes is virtually nothing)
I bet, you can't measure a difference between PVector and your own class PVectorSimple
;-)
but there are, roughly, 11 places that could be replaced by pvectors. so that's 44 bytes wasted 8)
what's with those occasional radial lines? they look like they shouldn't be there.
float
& 1 array reference.According to https://www.SlideShare.net/cnbailey/memory-efficient-java
An object's header is indeed 12 bytes for 32-bit Java:
And it doubles for 64-bit. That is, 24 bytes total for its header:
Since I'm using the 64bit PDE's version, each PVector is now: 24 + 4*4 = 24 + 16 = 40 bytes.
No need for 8-bit alignment padding this time like the 32bit version. =P~
But still, for 32bit Java PDEs, removing the 4th useless array[] field would net 8 bytes off, from 32 bytes to 24 bytes for each PVector object. [-(
So each appearance 8 bytes, so approx. 11*8 bytes = 88 bytes saved.
I still don't think that's a lot. But thank you anyway.
it's 24 bytes, total, for a PVector according to this:
http://www.javaworld.com/article/2077408/core-java/sizeof-for-java.html
8 overhead, 4 for each float, 4 for the array ref.
https://Gist.GitHub.com/arturmkrtchyan/43d6135e8a15798cc46c
a) Class PVector for 32-bit Java PDE: (Header = 8 bytes (64 bits))
8 + 4*4 = 8 + 16 = 24 bytes total. No padding needed!
If we theorize it w/o the 4th extra field we'd get:
8 + 3*4 = 8 + 12 = 20 bytes + 4 padding = 24 bytes total. No difference due to 8-byte align padding!
b) Class PVector for 64-bit (compressed) Java PDE: (Header = 12 bytes (96 bits))
12 + 4*4 = 12 + 16 = 28 bytes + 4 padding = 32 bytes total.
Theorizing again w/o the 4th extra array[] field we get now:
12 + 3*4 = 12 + 12 = 24 total. No padding needed!
Difference is 32 - 24 = 8 bytes off!
So for 64-bit Java w/ compressed addresses turned on (default), we're wasting 8 bytes for each PVector object created due to its extra useless 4th field! X(
Whether or not to use PVector -- and both approaches are valid -- is probably a readability / maintainability issue, not a performance issue.
A sketch with a PVector "wastes" 8 bytes. A sketch with 10,000 PVectors uses an extra 80 kilobytes. This is still nothing -- many orders of magnitude away from ever becoming a memory optimization concern even on a mobile system. Saying otherwise seems like a classic example of premature optimization.
Show me a sketch with millions of PVectors and a performance bottleneck, and then let's talk about replacing a class to save 8 bytes.
Good point. Nonetheless, for obvious reasons, it is not a good programming practice to have such wasteful variables in a program. For example, you could just add a few extra but mostly useless variables to your own custom class, but it won't be sensible to do so.
I was specifically ranting about the 4th array[] 100% unused field inside PVector class.
But if we compare 3
float
x, y & z "unbound" fields against 1 PVector "encapsulated" x, y & z fields, plus its empty array[] field, the diff. isn't 8 bytes only! [-XEach
float
is 4 bytes. So 3float
fields = 12 bytes only.But if we replace those 3
float
fields w/ 1 PVector object, as I had already calculated in my previous answer, it's 32 bytes each for the 64bit Java PDE! :-&The encapsulation cost is then: 32 bytes - 12 bytes = 20 wasted bytes! :-t
And even worse, if we also count the field variable which stores the PVector object, we got an extra cost of 4 bytes! @-)
So it's not 32 bytes, but actually 36 bytes! b-(
And wasted bytes are now 24, for each time we choose 1 PVector over 3
float
fields!!! :-hIn simpler words, sometimes you have to make your code less efficient just to make it easier to understand.
So doing that saved in total 14 lines of code. Using the PVector class brings other problems, as it uses the float data type: for (int x = xInterval.x; x < xInterval.y; x += rings) { Needs to be casted or something: for (int x = (int)xInterval.x; x < xInterval.y; x += rings) {
Do you guys really think the code is prettier now?
P.S. To prevent "@params" from becoming a hyperlink, put a space between @ and params. Like this -
@ params
.The code is quite pretty looking right now. :)>-
Can someone explain why the function settingsChange isn't true if I change the PVector types? E.g. does something like this in draw(): plot.ratio.x += 2; It changes both old_ratio.x and ratio.x ???
settingschanged() works with other variable types like int ring;
@ line #183
old_ratio = ratio;
, you're making old_ratio to point to the same PVector object reference which ratio already points to! :-@If your intention is for each 1 to point to its own PVector object, you're gonna need to clone ratio's w/ get() or copy(), and assign that clone to old_ratio: *-:)
old_ratio = ratio.get();
You can't use == on objects. Use .equals() instead.
Probably also related to the fact that pvector is an object. Sounds like you are assigning one pvector to another, so they now both refer to the same object.
@GoToLoop Arh, okay. So to clarify: I'm wrongfully deleting/freeing the old memory that the PVector held and setting it to point towards the same location in the RAM as the new one?
@koogs I was checking each of the PVectors fields, but equals() is nicer, thanks.
@0xDEADBEEF, let's analyse the lifespan of 1 PVector object within your sketch, shall we? O:-)
PVector ratio = new PVector(20, 5);
new
PVector is instantiated, and thus anew
derived object is created outtaclass
PVector.150 000
.150 000
, pointing to thenew
PVector object.plot = new plot3D(center, xInterval, ratio, vertices, rings, drawMethod);
, a plot3D object is created at some other memory block location.150 000
, that's exactly the value which is received by that constructor.ratio = _ratio;
, we can see constructor's parameter _ratio is assigned to plot3D's field ratio.150 000
from setup()'s local variable ratio, and it is consequently assigned to plot3D's field ratio, the latter is now pointing to that very same PVector object created within setup() at memory address150 000
. #:-Sold_ratio = ratio;
.150 000
it had received within constructor block, now field old_ratio is assigned that same value.This issue can be tackled with
copy()
?also those lines can be simplified
like
?
not tested