Testing Ruby VALUES in C-extension.
-
So I finally managed to bring in 100 000 points convert them to C++ class and then convert back to Sketchup Point3d objects.
Timing the response back in Alex S-Ruby console clocks ~ 0.50 which is amazingly fast.
Did some other tests, bloody hell it's blitz..However if I clock a new "puts time" in the row after the C -callback. it gets increasingly
slower for each C-functioncall.
C-callback response still clocks about the same each time..Is it the Ruby garbage collector running ? Adding this amount of points aught to slow Sketchup down, I know..
So I take it one cannot rely on C-response when things are "ready" to go forth in Ruby ?
For ex if I expect an array as responce will there be an error in following code.
Or does not following code execute until the array is ready ?
(havent tested stuff on so many points yet)I don't think I'm creating a memory leak in C++ since I have seen the destructor activate during some tests. And this test is done within 1 function so.. Points in points out..
Note: I did this in debug-mode(SU 2013), so maybe that affects threading or something.
-
@jolran said:
Timing the response back in Alex S-Ruby console clocks ~ 0.50 which is amazingly fast.
Did some other tests, bloody hell it's blitz..@jolran said:
Note: I did this in debug-mode(SU 2013), so maybe that affects threading or something.
Debug builds are a lot slower than release as it execute a lot more debug code and doesn't have any optimizations enabled.
@jolran said:
However if I clock a new "puts time" in the row after the C -callback. it gets increasingly
slower for each C-functioncall.
C-callback response still clocks about the same each time..Is it the Ruby garbage collector running ? Adding this amount of points aught to slow Sketchup down, I know..
Hm... not sure. Might be. When I run performance tests in Ruby I tend to call GC.start at the beginning of my profiling to ensure the GC is reset before each test. Otherwise you have no idea when it will trigger.
@jolran said:
So I take it one cannot rely on C-response when things are "ready" to go forth in Ruby ?
For ex if I expect an array as responce will there be an error in following code.
Or does not following code execute until the array is ready ?
(havent tested stuff on so many points yet)I don't think I'm creating a memory leak in C++ since I have seen the destructor activate during some tests. And this test is done within 1 function so.. Points in points out..
Your tests, does it only create Point3ds, or are you also creating and erasing geometry?
-
Hi Thomas.
For that test I was sending in Ruby array with Sketchup Point3d's to a C++ function.
That function converted Ruby points to a simple C++ class representing Point3d.
That same function also converted back the C++ points to Sketchup Points.
So the return VALUE was a Ruby array of those points.
Not very useful, but interesting. Got to start somewhere...I use vectors as containers in C++, don't know if that is the way to go with Ruby C++-extensions. Vectors seam to be quite used though..
Do you use maps with Ruby hashes ? I havent tested yet. Vectors seams more flexible.What you say about Debug builds seams to hold true.
I managed to create an implementation for the points-indexing-triangulation I was talking about earlier, and ran that in my plugin compiled. Really fast compared to the Ruby version
It still has some quirks and needs refinements, but it was good enough to make a realistic comparison.@unknownuser said:
When I run performance tests in Ruby I tend to call GC.start at the beginning of my profiling to ensure the GC is reset before each test. Otherwise you have no idea when it will trigger
Ahhh yes. That's smart. Will try that, thanks.
Edit: Ehh duh! I wasent aware one could create breakpoints and step into the code while debugging in Sketchup. Most useful
-
@jolran said:
I use vectors as containers in C++, don't know if that is the way to go with Ruby C++-extensions. Vectors seam to be quite used though..
Do you use maps with Ruby hashes ? I havent tested yet. Vectors seams more flexible.Yes, vectors are your usual go-to container.
map and unordered_map is what one use instead of Ruby Hash. Unordered map is usually a little bit faster, but a C++11 feature.
set and unordered_set is similar to - surprise - Ruby Set.@jolran said:
Edit: Ehh duh! I wasent aware one could create breakpoints and step into the code while debugging in Sketchup. Most useful
One thing that cought me off guard is the breakpoints doesn't always work in Release due to compiler optimization. Visual Studio 2013 even has a nice profiler which let you get detailed profiling info of your C++ code. (Should do a quick tutorial on that once.)
Speaking of debuggers, you know taht from SU2014 you can hook up Ruby debuggers for Ruby code in SketchUp?
https://github.com/SketchUp/sketchup-ruby-debugger
It's so much nice to be able to set breakpoints and step than litter the code with puts. -
Btw, when you create a vector where you know the final number of item, call reserve to allocate enough memory in advance as you then avoid expensive memory reallocation as you populate the array. Even if you have only a rough estimate it's ok.
You can do something similar C++ std::vector<>.reserve with the Ruby C API by calling rb_ary_new2. You provide the number of final elements and it will allocate enough memory of the object up front.
-
@unknownuser said:
Visual Studio 2013 even has a nice profiler which let you get detailed profiling info of your C++ code. (Should do a quick tutorial on that once.)
Yes that would off course be very nice.
Realized this thread is staring to get really packed of usefull information. Unfortunately in some time it will be burried in the forum.
Maybe a C-extension sticky-thread or something similar wouldent hurt. Or maybe this old topic is being used for that purpose ?
http://sketchucation.com/forums/viewtopic.php?f=180%26amp;t=41077%26amp;p=363978One might not feel inclined to post silly C++ questions in that old thread.
Something like "Ruby C extensions for dummies" would be more approprioate. -
@unknownuser said:
One thing that cought me off guard is the breakpoints doesn't always work in Release due to compiler optimization. Visual Studio 2013 even has a nice profiler which let you get detailed profiling info of your C++ code. (Should do a quick tutorial on that once.)
Speaking of debuggers, you know taht from SU2014 you can hook up Ruby debuggers for Ruby code in SketchUp?
https://github.com/SketchUp/sketchup-ruby-debugger
It's so much nice to be able to set breakpoints and step than litter the code with puts.Well this keeps gets better and better. I did not know that.
Have been sending puts to ruby console from C++ but that wont work when stepping over code, so this sounds great.@unknownuser said:
You can do something similar C++ std::vector<>.reserve with the Ruby C API by calling rb_ary_new2. You provide the number of final elements and it will allocate enough memory of the object up front.
Handy! I do a lot of conversions so that will help. I'm not in a mode of optimizing code yet, but suppose best practices are worth picking up at an early stage.
I'm experiencing some problems in a loop here. Don't know if you can spot whats going on..
Have just included the important parts for brevity.I have a nested ruby array of points curves. The points are coming from a getter-method-call in a Ruby-class. The @points can be of vary length.
The problem is that RARRAY(curve_rbPoints)->len always gives the same length, so the code fails..
Is it because I'm hoisting the variable ?
I just found this bug so havent tested much yet..NOTE: I should mention it get's the correct length 1st time round.
edit: nope.// VALUE curves = [ curve, curve, etc ] // @points = [point3d, point3d, etc] const VALUE get_points = rb_intern("get_points"); //getter method for @points int curves_size = RARRAY(curves)->len; VALUE curve, curve_rbPoints; int rCrv_len, i; // Inside a loop for (i = 0; i < curves_size; i++) { curve = rb_ary_entry(curves, i); //My class Ec-curve instance" curve_rbPoints = rb_funcall(curve, get_points, 0); //Gets @points in current curves[index] rCrv_len = RARRAY(curve_rbPoints)->len; //WRONG LENGTH! //Nested for loop is coming based on rCrv_len length
-
@jolran said:
I have a nested ruby array of points curves. The points are coming from a getter-method-call in a Ruby-class. The @points can be of vary length.
The problem is that RARRAY(curve_rbPoints)->len always gives the same length, so the code fails..
Is it because I'm hoisting the variable ?
I just found this bug so havent tested much yet..NOTE: I should mention it get's the correct length 1st time round.
edit: nope.That only works with older Ruby I think, 1.8.6 or earlier. After that they changed the data structure so you should use the RARRAY_LEN macro. That macro doesn't exist on early Ruby so I conditionally define it:
#ifndef RARRAY_LEN #define RARRAY_LEN(x) (RARRAY(x)->len) #endif
When you look up info on Ruby C Extension programming make sure you check the date of the source. And refer to a recent version of README.EXT.
I also find a lot of clues in "Ruby MRI identifier search": http://rxr.whitequark.org/mri/ident
-
Ahhh. A solution perhaps
I'm running Sketchup 2013 in debug mode. So it should be running Ruby 1.8.6 though ?This has been a very annoying problem to solve. I'm clueless.
There's no error but irratic behavior with wrong values which makes it hard to track.
I was thinking it had something to do with me calling instance method from a class
sort of thing. The "curve" is an instance from a class I created and not a Sketchup curve, but I assume you got that part.I suppose I could make the Ruby array an accessor and then use instance var get, but I rather not.
Will try your code when I get to my workstation later.
Thanks!
-
I don't see anything immediately wrong. But have you checked and validated the input data?
Feels like there's some issue not located to that small snippet you have. If a complex case fails, try to reproduce in a small standalone case.Btw, rb_intern returns ID, not VALUE. At least in Ruby 1.8 they are typedef of the same, but you cannot be sure of that and should make sure when a function use ID vs VALUE.
-
Also, you're using out C++ example? Then you don't have to declare all variables in advance. That's very old C syntax. Even newer C syntax allow you to declare variables when you need them.
-
I am doing a C++ extension, yes.
@unknownuser said:
rb_intern returns ID, not VALUE
Well that changes things. That could be it. Will test that in various combinations and report back.
So would the syntaxrb_funcall(rb_intern("get_points")etc... be better then instead of storing it ?
@unknownuser said:
try to reproduce in a small standalone case.
I have created a smallish testcase. Although I needed some classes for that.
So the code become over 500 lines.
I run that code in Alex ruby code editor while debug. Don't know if that can affect the scope of class or anything.And I have tested the data before sending in to c-extension, it's alright in Ruby.
The C++ code may very well have logic errors I havent found yet. So I have to scan that through again. It looks like this is the main problem though, since I'm dependent of array length for next iterations. When all the arrays(curves) have same length code is working.
@unknownuser said:
That's very old C syntax. Even newer C syntax allow you to declare variables when you need them.
I couldent tell whats proper syntax or not, yet. That does sound easier.
Does that go for loops/iterators as well? -
@jolran said:
@unknownuser said:
rb_intern returns ID, not VALUE
Well that changes things. That could be it. Will test that in various combinations and report back.
Don't think it does any harm in Ruby 1.8 and 2.0 as both happen to be typedef of unsigned long - the compiler won't know the difference. So I don't think that is your issue here.
But if the Ruby C API changes what types they map to you will get trouble.@jolran said:
I couldent tell whats proper syntax or not, yet. That does sound easier.
Does that go for loops/iterators as well?[/quote]
Best practices usually states to declare variables right before you need them and scope them as appropriate.
I'm not quite sure what you are asking for about loops and iterators though. -
I meant hoisting variables before running loops. Speeding up the loops by not declaring them inside the loop.
Changing to rb_funcall(curve, rb_intern("get_points"), 0); made no difference, so I will try that other macro you mentioned. Otherwise there must be some hole in my logic elsewhere..
PS: I havent come to best practices part yet
Thanks for the help.
-
Well well. How dumb can one get seriously
Have been copying and pasting in code from Notepad++ to sketchup ruby code editor.
Totally unfocused on that part..
At some point I must have pushed ctrl v instead of ctrl c, so I not noingly had duplicate code.Off course I wasent editing the first part that was sent in to Visual studio for step over.
Hehum, thanks for the help Thomthom. All is not lost. You have provided some good information for the rest of us and I learned some new stuff
-
@jolran said:
I meant hoisting variables before running loops. Speeding up the loops by not declaring them inside the loop.
That's a Ruby world optimization - because creating Ruby objects are expensive.
Not the same in C++.So now you're getting the correct array size?
-
@unknownuser said:
That's a Ruby world optimization - because creating Ruby objects are expensive.
Not the same in C++.Ok understood. It's quite wierd comming from Ruby to C++.
@unknownuser said:
So now you're getting the correct array size?
Yes. And the code work's as I wanted to So far anyway. Really fast.
I included your MACRO just to be safe. The code worked compiled in SU8.I imagine I could even make it faster refactoring it. But.. Most importantly I want it to be proper, so I try to follow your advice and read up on best practices before I move on.
On another different subject while we are at it. Probably should start a new topic, but it doesent fell improper to ask here as well.
Can we use Windows forms or WXwidgets(not WX-rubyversion) or other GUI toolkit with c++ in Sketchup ?
I've seen discussion about it, but it was long time ago and relevant to only Ruby.I've seen examples where they hook up MFC as resource to win32 projects. Also read here somewhere it can be problematic to hook that up to Sketchup window, since windialogs are under the desktop(?)
Anyway, not an substitute for webdialogs, just could be handy for some simpler dialogs using visual studio dialog editor. -
I can't speak for WXwidgets, not familiar with it. Though I seem to recall some issues where they changed the order the window hierarchy - but maybe that was just the Ruby wrapper, I don't know.
But in general you can use whatever C/C++ lib you want. Native dialogs, knock yourself out. I haven't done so myself yet, but I wanted to try out a simple hello world window once.
I have done some code that calls the system folder picking dialogs though. Works very well. -
@unknownuser said:
I can't speak for WXwidgets, not familiar with it. Though I seem to recall some issues where they changed the order the window hierarchy - but maybe that was just the Ruby wrapper, I don't know.
I think it was the Ruby wrapper that was causing problems.
The posts touching the subjects are very old, as mentioned..@unknownuser said:
But in general you can use whatever C/C++ lib you want. Native dialogs, knock yourself out. I haven't done so myself yet, but I wanted to try out a simple hello world window once.
I have done some code that calls the system folder picking dialogs though. Works very well.I thought it was a no go on that! I'll peek around a little then.
Report back if I find something exiting.Thanks
-
I've just read all of this - very helpful and interesting.
Years ago I had a project that had to work with 3 compilers, CE, Visual C++ and C++Builder and I had a single code base.
I ended up learning new optimization procedures which when used right from the start created no extra work. For CE and risc processors you must design data structures that are memory aligned. This not only runs fine on cisc - it is automatically optimized. The key bit here is that if a 4 byte int crosses a memory boundary then cisc does 2 fetches and stitches it together, risc simply crashes.
I created a tList object that works with all 3 compilers. You can treat a tList as an array or a hash or a set. You can pass in objects and perform binary tree searches on any member of the object by simply passing it a function pointer to your own custom compare function. This is useful when you want many sort combinations on a single collection. This is a quicksort implementation.
How the tList works is it is essentially an array of pointers. The array is in contiguous memory. The array grows in a very specific way and is moved if you run out of free contiguous memory. When the tList object is first created you can provide an initial number of elements that you want thus avoiding repeated calls to malloc and avoiding moving memory. When sorting just the list of pointers sort - the collected objects stay where they are.
Here is a compare function that sorts on 2 fields. If first field is a tie then sort on second field
The void pointer is cast as the type of object that you collect in the tList.
fIndex is 1 or -1 which changes the sort from ascending to descendingint __fastcall CompareCabsCabinet( void * Item1, void * Item2 ) { int res; tCab *v1 = ( tCab *) Item1; tCab *v2 = ( tCab *) Item2; res = strcmp( v1->Description.c_str(), v2->Description.c_str() ); if ( res == 0 ) res = v1->CabNumber - v2->CabNumber; return fIndex * res; } //---------------------------------------------------------------------------
When I wrote a GIS rendering engine I used a tList for all the points. I added a quad tree for static map data and an rtree for dynamic gps points.
I ended up with virtually instant renderings with a million points.
Advertisement