NXC code to NBC code

Discussion specific to NXT-G, NXC, NBC, RobotC, Lejos, and more.
nxtboyiii
Posts: 366
Joined: 02 Oct 2010, 07:08
Location: Everywhere

NXC code to NBC code

Post 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
Thanks, and have a nice day,
nxtboy III

programnxt.com
muntoo
Posts: 834
Joined: 01 Oct 2010, 02:54
Location: Your Worst Nightmare
Contact:

Re: NXC code to NBC code

Post by muntoo »

Always optimize the algorithm first (like Knuth!), then the code.
Image

Commit to LEGO Mindstorms Robotics Stack Exchange:
bit.ly/MindstormsSE


Commit to LEGO Stack Exchange: bit.ly/Area51LEGOcommit
nxtboyiii
Posts: 366
Joined: 02 Oct 2010, 07:08
Location: Everywhere

Re: NXC code to NBC code

Post by nxtboyiii »

Can you post some NBC code of the NXC code in NBC?
Thanks, and have a nice day,
nxtboy III

programnxt.com
muntoo
Posts: 834
Joined: 01 Oct 2010, 02:54
Location: Your Worst Nightmare
Contact:

Re: NXC code to NBC code

Post 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.
Image

Commit to LEGO Mindstorms Robotics Stack Exchange:
bit.ly/MindstormsSE


Commit to LEGO Stack Exchange: bit.ly/Area51LEGOcommit
nxtboyiii
Posts: 366
Joined: 02 Oct 2010, 07:08
Location: Everywhere

Re: NXC code to NBC code

Post by nxtboyiii »

Wow. :shock: Thats a lot of stuff you said! Thanks muntoo!! :D
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!! :)
Thanks, and have a nice day,
nxtboy III

programnxt.com
muntoo
Posts: 834
Joined: 01 Oct 2010, 02:54
Location: Your Worst Nightmare
Contact:

Re: NXC code to NBC code

Post 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?
Image

Commit to LEGO Mindstorms Robotics Stack Exchange:
bit.ly/MindstormsSE


Commit to LEGO Stack Exchange: bit.ly/Area51LEGOcommit
spillerrec
Posts: 358
Joined: 01 Oct 2010, 06:37
Location: Denmark
Contact:

Re: NXC code to NBC code

Post 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?
My blog: http://spillerrec.dk/category/lego/
RICcreator, an alternative to nxtRICeditV2: http://riccreator.sourceforge.net/
muntoo
Posts: 834
Joined: 01 Oct 2010, 02:54
Location: Your Worst Nightmare
Contact:

Re: NXC code to NBC code

Post 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... :(
Image

Commit to LEGO Mindstorms Robotics Stack Exchange:
bit.ly/MindstormsSE


Commit to LEGO Stack Exchange: bit.ly/Area51LEGOcommit
nxtboyiii
Posts: 366
Joined: 02 Oct 2010, 07:08
Location: Everywhere

Re: NXC code to NBC code

Post 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!!
Thanks, and have a nice day,
nxtboy III

programnxt.com
muntoo
Posts: 834
Joined: 01 Oct 2010, 02:54
Location: Your Worst Nightmare
Contact:

Re: NXC code to NBC code

Post 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);.
Image

Commit to LEGO Mindstorms Robotics Stack Exchange:
bit.ly/MindstormsSE


Commit to LEGO Stack Exchange: bit.ly/Area51LEGOcommit
Post Reply

Who is online

Users browsing this forum: No registered users and 3 guests