From: Joe Van Dyk Date: 2005-07-13T09:58:00+09:00 Subject: Re: Updating GUIs On 7/12/05, Joe Van Dyk wrote: > On 7/12/05, Joe Van Dyk wrote: > > On 7/12/05, Lothar Scholz 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; > } > > I somehow figured out how to do the following in C: player.polygon = player.polygon.collect do |x,y| rotate(player.heading, x, y, player.x, player.y) end So, now that Ruby code looks like player.polygon = Rotate::rotate_points(player.polygon, player.heading, player.x, player.y) And the C function for Rotate::rotate_points looks like: static VALUE rotate_points(VALUE self, VALUE points, VALUE heading, VALUE x, VALUE y) { int i; VALUE return_array = rb_ary_new(); for (i = 0; i < RARRAY(points)->len; i++) { rb_ary_push( return_array, rotate(self, heading, RARRAY(RARRAY(points)->ptr[i])->ptr[0], RARRAY(RARRAY(points)->ptr[i])->ptr[1], x, y)); } return return_array; } Time is now down from 2 seconds in the original Ruby version to 0.66 seconds in the new C version (on a very slow computer). I'm still iterating over each Player in Ruby though. Any other ideas on how I can improve the speed?