Joe Van Dyk
7/12/2005 11:56:00 PM
On 7/12/05, Joe Van Dyk <joevandyk@gmail.com> wrote:
> On 7/12/05, Lothar Scholz <mailinglists@scriptolutions.com> wrote:
> > Hello Joe,
> >
> > JVD> I'm doing this on a TkCanvas.
> >
> > If you use TkCanvas it is very unlikely that the speed problem is
> > caused by the update call. This part is very optimized in TK.
> >
> > JVD> Should I try to convert the rotate function to C and then see if it's
> > JVD> fast enough? I'm still making a gazillion function calls (say, 300
> > JVD> polygons and 30 points each and update twice a second, that's 18,000
> > JVD> function calls a second). No wonder it's slow. :( Perhaps using
> > JVD> TkCanvas was a mistake for this.
> >
> > Have you isolated your rotation code and calculated the values without
> > passing the parameters to TkCanvas. How fast is this ?
> >
> > The only TK related problem is to pass 18000 floating point numbers to
> > the canvas widget. Don't know how heavyweight the RubyTK layer is but
> > it means converting them to strings and back to floating points. So
> > maybe this parameter setting is the reason for the problem, but you
> > must measure it not trying to guess it.
> >
> > I've seen more complicated TkCanvas scenarios 7 years ago when
> > CPU speed was 400 MHz and TK did not show a problem.
>
> This is taking me about a third of a second to do. Seems a bit high
> if I want to do this at least twice a second and leave room on the
> machine to do other things:
>
>
> # rotate function
> def rotate(deg, x, y, c_x = 0, c_y = 0)
> rad = (deg * Math::PI)/180.0
> s_rad = Math::sin(rad)
> c_rad = Math::cos(rad)
> x -= c_x
> y -= c_y
> [c_x + (x * c_rad - y * s_rad), c_y + (x * s_rad + y * c_rad)]
> end
>
> class Player
> attr_accessor :x, :y, :polygon, :heading
> def initalize
> @x = @y = @heading = 0
> @polygon = []
> end
> end
>
> players = []
> 500.times do
> p = Player.new
> p.x = rand 1000000
> p.y = rand 1000000
> p.heading = rand 360
> p.polygon = [] # Why do I need this here? If it's not, p.polygon is nil. :(
> 40.times do
> p.polygon << [rand(10), rand(10)]
> end
> players << p
> end
>
> start_time = Time.now
> players.each do |player|
> player.polygon = player.polygon.collect do |x,y|
> rotate(player.heading, x, y, player.x, player.y)
> end
> end
> end_time = Time.now
> puts end_time - start_time
>
>
> So what's the next step when trying to optimize this? Convert the
> rotate function to C? Or can I use a different algorithm?
On a very fast machine, the above code ran in 0.14 seconds. On a
pentium1 machine, it ran in 2.04 seconds.
When I used the following C function to do the rotation, it pretty
much halved the execution time to 0.06 seconds on the fast machine and
0.94 seconds on the Pentium1 machine.
I'm pretty new to C extensions, is there anything else I can do to
improve the speed? It would seem that moving the code that loops
around the Players and collects the polygon points to C would help
much more, but I don't know how to do that yet in C.
static VALUE rotate(VALUE self, /* Module */
VALUE _degree, /* Rotate Degree */
VALUE _x, VALUE _y, /* Point */
VALUE _c_x, VALUE _c_y) /* Center to rotate around */
{
double degree = NUM2DBL(_degree);
long x = NUM2LONG(_x);
long y = NUM2LONG(_y);
long c_x = NUM2LONG(_c_x);
long c_y = NUM2LONG(_c_y);
double rad = (degree * M_PI) / 180.0;
double s_rad = sin(rad);
double c_rad = cos(rad);
x -= c_x;
y -= c_y;
VALUE return_array = rb_ary_new();
rb_ary_push(return_array, rb_float_new(c_x + (x * c_rad - y * s_rad)));
rb_ary_push(return_array, rb_float_new(c_y + (x * s_rad + y * c_rad)));
return return_array;
}