Page 1 of 4
NXC code to NBC code
Posted: 11 Jun 2011, 14:37
by nxtboyiii
Hi,
Can someone convert this NXC code into NBC code so it will run faster?
I will just use the asm inside the NXC code.
Here it is:
Code: Select all
for(int c=floor(xadd/-16); c < ceil(xadd/-16)+6; c++)
{
CircleOut(x+8,y+8,8,DRAW_OPT_FILL_SHAPE);
for(int d=floor(yadd/-16); d < ceil(yadd/-16)+4; d++)
{
var[0]=map[c][d].x;
var[1]=map[c][d].y;
GraphicOutEx(c*16+xadd,d*16+yadd,"gmtilesb.ric",var);
}
}
Thanks,
nxtboy III
Re: NXC code to NBC code
Posted: 11 Jun 2011, 18:31
by muntoo
Always optimize the algorithm first (like Knuth!), then the code.
Re: NXC code to NBC code
Posted: 11 Jun 2011, 22:36
by nxtboyiii
Can you post some NBC code of the NXC code in NBC?
Re: NXC code to NBC code
Posted: 12 Jun 2011, 01:23
by muntoo
I don't know about NBC, but here's a slightly more micro-optimized version:
Code: Select all
// These remain "constant"
int maxval_x = ceil(xadd / -16) + 6;
int maxval_y = ceil(yadd / -16) + 4;
int init_c = floor(xadd / -16); // This one's actually unnecessary... But let's keep it consistent.
int init_d = floor(yadd / -16);
int circle_x = x + 8;
int circle_y = y + 8;
int init_c2 = (init_c << 4) + xadd; // init_c << 4 is the same as init_c * pow(2, 4) or init_c * 16
int init_d2 = (init_d << 4) + yadd;
// Relative/Delta Steps
int c2 = init_c2;
int d2 = init_d2;
CircleOut(circle_x, circle_y, 8, DRAW_OPT_FILL_SHAPE); // WHY do you keep redrawing this??
for(int c = init_c; c < maxval_x; ++c)
{
d2 = init_d2;
for(int d = init_d; d < maxval_y; ++d)
{
var[0] = map[c][d].x;
var[1] = map[c][d].y;
GraphicOutEx(c2, d2, "gmtilesb.ric", var);
d2 += 16; // Take a "delta step".
}
c2 += 16; // Take a "delta step".
}
Converting to NBC doesn't magically make it faster. Sure, NXC compiles the
for
-loops as fail-safe for every case (this could be optimized slightly), but you should really look at the actual algorithm. (In this case, I admit that it may be harder to find a "better" algorithm.)
A few micro-optimization tricks for bottlenecks;
- Are you reevaluating unchanging values? If so, put them in a temporary variable. (Such as
maxval_x
.)
- If you look at the NBC code for
CircleOut()
, you'll see that it's copying the values to a DrawCircleType
struct every time. Spiller did something similar in his Bezier Curve program when he also took the sacred route of NXC->NBC. What I recommend instead: using the SysDrawCircle()
function directly in your NXC code. Same with SysDrawGraphicEx()
.
- Notice any patterns? Can you change variables' values using relative/delta steps, instead of "absolute evaluation" (mathematicians love the "absolute" stuff, but theoretical computer scientists don't).
Here it is with the
Sys
stuff (
post under construction):
Code: Select all
// These remain "constant"
int maxval_x = ceil(xadd / -16) + 6;
int maxval_y = ceil(yadd / -16) + 4;
int init_c = floor(xadd / -16); // This one's actually unnecessary... But let's keep it consistent.
int init_d = floor(yadd / -16);
int circle_x = x + 8;
int circle_y = y + 8;
int init_c2 = (init_c << 4) + xadd; // init_c << 4 is the same as init_c * pow(2, 4) or init_c * 16
int init_d2 = (init_d << 4) + yadd;
// Relative/Delta Steps
int c2 = init_c2;
int d2 = init_d2;
// Drawing stuff
DrawCircleType dcArgs;
DrawGraphicType dgArgs;
// This is part of the code is unnecessary, unless you're putting it in a loop. I'll leave it here anyways, though.
dcArgs.Center.X = circle_x;
dcArgs.Center.Y = circle_y;
dcArgs.Size = 8; // radius
dcArgs.Options = DRAW_OPT_FILL_SHAPE;
SysDrawCircle(dcArgs);
// This part IS necessary.
dgArgs.Filename = "gmtilesb.ric";
ArrayInit(dgArgs.Variables, 0, 2);
// If that previous line doesn't work, use this instead:
// ArrayBuild(dgArgs.Variables, 0, 0);
dgArgs.Options = 0;
for(int c = init_c; c < maxval_x; ++c)
{
dgArgs.Location.X = c2;
d2 = init_d2;
for(int d = init_d; d < maxval_y; ++d)
{
// GraphicOutEx(c2, d2, "gmtilesb.ric", var);
dgArgs.Location.Y = d2;
dgArgs.Variables[0] = map[c][d].x;
dgArgs.Variables[1] = map[c][d].y;
SysDrawGraphic(dgArgs);
d2 += 16; // Take a "delta step".
}
c2 += 16; // Take a "delta step".
}
Again, micro-optimizations make your code
less readable and
less maintainable. If you're sure that your code is causing slowdowns, and you're not going to be modifying it any time soon... well, I guess it's
OK.
Re: NXC code to NBC code
Posted: 12 Jun 2011, 03:37
by nxtboyiii
Wow.
Thats a lot of stuff you said! Thanks muntoo!!
You are really experienced with this!!
Actually, variables xadd, yadd, circlex,and circley DO change. They are not constant. Thats a good idea to put the constants like the drawing functions before the loop so they are not repeated.
BTW, I keep redrawing the circle because the GraphicOutEx overwrites it.
Thanks!!
Re: NXC code to NBC code
Posted: 12 Jun 2011, 05:11
by muntoo
nxtboyiii wrote:Actually, variables xadd, yadd, circlex,and circley DO change. They are not constant.
By that, I meant "constant" in the
for
-loop. As long as you don't modify them
inside the
for
-loop, it's OK.
nxtboyiii wrote:BTW, I keep redrawing the circle because the GraphicOutEx overwrites it.
Aha!
That's one thing you can optimize. Why don't you put
CircleOut()
after the
for
-loop? Is there any specific effect you're trying to achieve? Can the same effect, or another appropriate effect, be done less resource-intensively?
Re: NXC code to NBC code
Posted: 12 Jun 2011, 07:53
by spillerrec
As muntoo said, it is better to optimize the algorithm first.
In the code you provide, the GraphicOutEx() function (or the sys version for that sake) is probably what takes the most time. So if you need to call it a lot of times, it might be limited to how fast it can redraw. (That is why I started using RIC fonts where I could instead.)
Anyway, the stuff you really want to avoid is array handling, or more specifically, getting values out of an array.
I will have to leave for now, but if you would like to present a working program it would be easier for us to help optimizing it. Especially, what does the map[][] array contain and how is the RIC file constructed?
Re: NXC code to NBC code
Posted: 12 Jun 2011, 17:23
by muntoo
Whenever you use
GraphicOutEx()
, your program:
- [Re-]Reads the RIC file into a temporary buffer
- [Re-]"Parses" it
- Draws new tile
The first one could be eliminated by using
GraphicArrayOutEx()
:
Code: Select all
byte ric_gmtilesb_data[];
void ric_init()
{
byte handle;
unsigned int fsize;
OpenFileRead("gmtilesb.ric", fsize, handle);
ReadBytes(handle, fsize, ric_gmtilesb_data);
CloseFile(handle);
}
task main()
{
// Run ONCE.
ric_init();
// Code here
GraphicArrayOutEx(x, y, ric_gmtilesb_data, vars, DRAW_OPT_NORMAL);
}
The second one could be eliminated using
SCR_File_Lib, but unfortunately, I don't think the blitting is as fast as the
GraphicArrayOutEx()
(which uses firmware opcodes, or something like that), so I wouldn't recommend it...
Re: NXC code to NBC code
Posted: 12 Jun 2011, 21:13
by nxtboyiii
The map[][] is a 2D array of a structure that has these variables:
byte x;
byte y;
bool sld;
The RIC file is constructed by using a bunch of 16x16 pictures. The Copybits has the input of the x and y of what 16x16 image is currently drawn.
I don't know how to use the ric data!!
Re: NXC code to NBC code
Posted: 12 Jun 2011, 21:35
by muntoo
nxtboyiii wrote:I don't know how to use the ric data!!
As I showed you in the code sample above, all you need to do is put
ric_init();
once at the beginning of your code. Then, use
GraphicArrayOutEx(x, y, ric_gmtilesb_data, vars, DRAW_OPT_NORMAL);
instead of
GraphicOutEx(x, y, "gmtilesb.ric", vars, DRAW_OPT_NORMAL);
.