Purging Instance Variables

I’m trying to understand why my plugin slows down sometimes after multiple operations of various tools and functions within the plugin. After a while the performance seems to get sluggish. I’ve noticed this dramatic decrease in performance especially with SU 2017 Make.

I’m trying to run a few tests to figure out what is causing the issue.

One thought I’ve had is that utilize a number of instance variables so that I can pass information easily across multiple methods without having to explicitly send those variables as arguments into those methods.

After my operation wraps up, these variables are still hanging around, eating up memory. Is there a way to purge these variables? Thoughts?

Hi @medeek,

You can just reset their values to nil, that way the garbage collector can do its job releasing unused object and clean memory.

If that doesn’t work enought quickly you can also ask the garbage collector to run with the GC.start command (Module: GC (Ruby 2.6.4)).

Hope it helps

1 Like

Try something like this …

# within a class or module

  def purge_instance_vars
    self.instance_variables.each do |sym|
      self.instance_variable_set(sym, nil) 
      self.remove_instance_variable(sym)
    end
    GC.start
  end
1 Like

Usually instance variables are held within an instance of a class that your code instantiates and populates with data pointed to by it’s instance variables. You can pass the instance’s reference and then the methods can access the ivars via accessor methods (ie “getter” method.)
When your code is done with this data, you dispose of the instance (by setting it to nil,) and garbage collection will dispose of all the mutable objects that the instance referenced. No need for a “hard purge”.

But Ruby has several ready built features you can use. (Yes I’ve told you of these in the past.)

You can code your own data class.

class Data
  attr_accessor(:name,:type,:material,:width,:height)
end

properties = Data.new
properties.name= "Garage Door"
# ... etc.
some_method_call(properties)

You can use Structs. You can use a Hash.

Or probably simplest would be an OpenStruct.

require 'ostruct'

properties = OpenStruct.new(
  name: "Garage Door",
  material: "Reinforced Metal",
  type: "Segmented Overhead Rollup",
  height: 9.feet,
  width: 20.feet
)

some_method_call(properties)

Anyway, when you’re done with the data, just set the data object to nil

properties = nil
# GC will clean up automatically

Or, if you created the data object within a method, when the method ends, the data object would “go out of scope” and become unreferenced and get cleaned up by GC anyway, with no need to explicitly set it to nil.

def create_garage_door
  properties = OpenStruct.new(
    name: "Garage Door",
    material: "Reinforced Metal",
    type: "Segmented Overhead Rollup",
    height: 9.feet,
    width: 20.feet
  )
  build_door_geometry(properties)
end

So, when create_garage_door() returns it’s local reference properties goes out of scope and will get cleaned up by GC (as long as build_door_geometry didn’t assign a persistent reference to it.)

1 Like

@medeek

It’s been a while since I’ve needed to work with it, but you might have a look at ObjectSpace, as I think you can look at what type of objects are currently ‘alive’.

1 Like

After further testing maybe the delay is due to something else but I can’t quite put my finger on it, and it’s very frustrating. I will hopefully get to the bottom of it.

The reason why I say this is that essentially the same methods and calls are made when I run a similar tool (but without an HTML menu), the “roof move edge” operation.

However, when this operation is run there is zero delay/lag, whereas when I run the “Edit Roof” function (with its HTML menu) the delay seems to get worse each time I run it.

I’m going to investigate the HTML dialog interaction with the javascript/HTML and see if the problem is somewhere buried in there.

At the moment I have no idea what is causing this issue.

I might also add that I’ve noticed this issue not just with the Truss plugin but with my other extensions when it comes to my edit functions (again with HTML menus). There seems to be a pattern here but I don’t know where it is pointing yet.

It may be related to all these instance variables hanging around but if that were the case why do the other tools not exhibit the same behavior?

I’ve eliminated the annoying timers in my javascript with a better methodology however the problem persists. Back to looking at what is going on under the hood with SketchUp.

I really have no idea how to effectively debug this problem, this one has me clearly stumped.

where do you create the :add_action_callback, it may have an effect on GC…

https://sketchucation.com/forums/viewtopic.php?f=180&t=40128#p355204

john

1 Like

First I’m going to see where exactly the bottleneck is:

t1 = Time.now 
...

t2 = Time.now
deltatime = t2 - t1
puts "roof extents generated #{deltatime}"

It just gets progressively slower:

main menu roof edit triggered.
points collected 0.004001
roof extents generated 0.009001
geometry complete 1.90911
draw complete 2.192126

main menu roof edit triggered.
points collected 0.01
roof extents generated 0.034002
geometry complete 5.166295
draw complete 5.77833

main menu roof edit triggered.
points collected 0.014001
roof extents generated 0.067004
geometry complete 9.523545
draw complete 10.568604

main menu roof edit triggered.
points collected 0.021002
roof extents generated 0.107007
geometry complete 15.784903
draw complete 17.473

main menu roof edit triggered.
points collected 0.037002
roof extents generated 0.17101
geometry complete 23.372337
draw complete 25.87148

main menu roof edit triggered.
points collected 0.044003
roof extents generated 0.246014
geometry complete 39.585264
draw complete 44.264532

I have my edit menu open and all I am doing is hitting the “Update” button which regenerates the roof assembly from scratch.

By the 6th regen it is almost up to 45 seconds to regen the roof. What would cause this? It must have something to do with the HTML dialog because the other functions without menus don’t do this progressive slow down. Very strange.

Just so you know I am testing this in SketchUp 2017 Make (my lowest common denominator). I’m not seeing as big of a problem in either 2018 or 2019. There are no errors being generated in the Ruby console, I’ve also fixed my timer issue in my javascript and did away with that silly methodology.

At this point I really don’t think it has anything to do with too many instance variables floating around. My roof edge move tool sets up the same variables and calls the same methods as the edit tool.

Its output shows no such signs of slowing down:

main menu roof edit triggered.
draw complete 0.302017

main menu roof edit triggered.
draw complete 0.295017

main menu roof edit triggered.
draw complete 0.293016

main menu roof edit triggered.
draw complete 0.321019

main menu roof edit triggered.
draw complete 0.306018

main menu roof edit triggered.
draw complete 0.304018

Here is my entire method that creates the HTML dialog and callbacks:

def show_edit_menu_polygon

	######################
	#
	# Html Menu
	#
	######################

			# Closed already open edit window if found open
			if @dlg002
				if @dlg002.visible?
					@dlg002.close
				end
			end

			if @Licensemode == "trial"
				if defined?(UI::HtmlDialog)
				@dlg002 = UI::HtmlDialog.new(
				{
  					:dialog_title => "Edit Roof Options - Version #{@Pluginversion} (trial version)",
  					:preferences_key => 'COMPLEX3',
  					:scrollable => true,
  					:resizable => true,
  					:width => 450,
  					:height => 900,
  					:left => 1200,
  					:top => 200,
  					:min_width => 50,
  					:min_height => 50,
  					:max_width =>1600,
  					:max_height => 1200,
  					:style => UI::HtmlDialog::STYLE_DIALOG
				})
				else
					@dlg002 = UI::WebDialog.new("Edit Roof Options - Version #{@Pluginversion} (trial version)", true, "COMPLEX2", 450, 900, 1000, 200, true)
				end
			else
				if defined?(UI::HtmlDialog)
				@dlg002 = UI::HtmlDialog.new(
				{
  					:dialog_title => "Edit Roof Options - Version #{@Pluginversion}",
  					:preferences_key => 'COMPLEX3',
  					:scrollable => true,
  					:resizable => true,
  					:width => 450,
  					:height => 900,
  					:left => 1200,
  					:top => 200,
  					:min_width => 50,
  					:min_height => 50,
  					:max_width =>1600,
  					:max_height => 1200,
  					:style => UI::HtmlDialog::STYLE_DIALOG
				})
				else
					@dlg002 = UI::WebDialog.new("Edit Roof Options - Version #{@Pluginversion}", true, "COMPLEX2", 450, 900, 1000, 200, true)
				end
			end


			#############################################


			@dlg002.add_action_callback("PUSHTOHTML_ROOF_EDIT") {|action_context, params|

				if @Unitstemplate == "metric"

					@BuildingWidth_m = (@BuildingWidth*@in_m).round(3)
					@BuildingLength_m = (@BuildingLength_ft*@in_m).round(3)
					@Overhang_m = (@Overhang*@in_mm).round(3)
					@Birdcut_m = (@Birdcut*@in_mm).round(3)
					@Rd_m = (@Rd*@in_mm).round(3)
					@Ply_m = (@Ply*@in_mm).round(3)
					@Rbd_m = (@Rbd*@in_mm).round(3)
					@Rbw_m = (@Rbw*@in_mm).round(3)
					@Hbd_m = (@Hbd*@in_mm).round(3)
					@Hbw_m = (@Hbw*@in_mm).round(3)
					@Vbd_m = (@Vbd*@in_mm).round(3)
					@Vbw_m = (@Vbw*@in_mm).round(3)
					@Rafter_spacing_m = (@Rafter_spacing*@in_mm).round(3)

					js_command = 'pushdata( "' + @Rooffamily.to_s + '|' + @Rooftype.to_s + '|' + @BuildingWidth_m.to_s + '|' + @BuildingLength_m.to_s + '|' + @Pitch.to_s + '|' + @Overhang_m.to_s + '|' + @Birdcut_m.to_s + '|' + @Rd_m.to_s + '|' + @Ply_m.to_s + '|' + @Rbd_m.to_s + '|' + @Rbw_m.to_s + '|' + @Hbd_m.to_s + '|' + @Hbw_m.to_s + '|' + @Vbd_m.to_s + '|' + @Vbw_m.to_s + '|' + @Rafter_spacing_m.to_s + '|' + @Framingoption.to_s + '|' + @Advroofoptions.to_s + '|' + @Assy_name.to_s + '|' + @Mats_in_library_html + '|' + @Mats_in_library_usage + '|' + @Input_color_db.to_s + '|' + @Templateunits.to_s + '|' + @Language_db.to_s + '|' + @Licensemode.to_s + '" );'
				else
					
					@BuildingLength_ft = (@BuildingLength / 12.0).round(5)
					@BuildingWidth_ft = (@BuildingWidth / 12.0).round(5)
	
					js_command = 'pushdata( "' + @Rooffamily.to_s + '|' + @Rooftype.to_s + '|' + @BuildingWidth_ft.to_s + '|' + @BuildingLength_ft.to_s + '|' + @Pitch.to_s + '|' + @Overhang.to_s + '|' + @Birdcut.to_s + '|' + @Rd.to_s + '|' + @Ply.to_s + '|' + @Rbd.to_s + '|' + @Rbw.to_s + '|' + @Hbd.to_s + '|' + @Hbw.to_s + '|' + @Vbd.to_s + '|' + @Vbw.to_s + '|' + @Rafter_spacing.to_s + '|' + @Framingoption.to_s + '|' + @Advroofoptions.to_s + '|' + @Assy_name.to_s + '|' + @Mats_in_library_html + '|' + @Mats_in_library_usage + '|' + @Input_color_db.to_s + '|' + @Templateunits.to_s + '|' + @Language_db.to_s + '|' + @Licensemode.to_s + '" );'
				end

				@dlg002.execute_script(js_command)	
			}

			@dlg002.add_action_callback("PUSHTOHTML_ROOF_EDIT_ADV") {|action_context, params|

				if @Unitstemplate == "metric"

					@Roofsheath_thk_m = (@Roofsheath_thk*@in_mm).round(3)
					@Roofclad_thk_m = (@Roofclad_thk*@in_mm).round(3)
					@Roofcladext_m = (@Roofcladext*@in_mm).round(3)
					@Fascia_width_m = (@Fascia_width*@in_mm).round(3)
					@Fascia_depth_m = (@Fascia_depth*@in_mm).round(3)
					
					js_command = 'pushdata_adv( "' + @Roofsheath_option.to_s + '|' + @Roofsheath_thk_m.to_s + '|' + @Roofsheathmat.to_s + '|' + @Roofclad_option.to_s + '|' + @Roofclad_thk_m.to_s + '|' + @Roofcladext_m.to_s + '|' + @Roofcladmat.to_s + '|' + @Fascia_option.to_s + '|' + @Fascia_type.to_s + '|' + @Fascia_width_m.to_s + '|' + @Fascia_depth_m.to_s + '|' + @Soffitcut.to_s + '|' + @Ridgecap_option.to_s + '|' + @Gutter_option.to_s + '" );'
				else
					
					js_command = 'pushdata_adv( "' + @Roofsheath_option.to_s + '|' + @Roofsheath_thk.to_s + '|' + @Roofsheathmat.to_s + '|' + @Roofclad_option.to_s + '|' + @Roofclad_thk.to_s + '|' + @Roofcladext.to_s + '|' + @Roofcladmat.to_s + '|' + @Fascia_option.to_s + '|' + @Fascia_type.to_s + '|' + @Fascia_width.to_s + '|' + @Fascia_depth.to_s + '|' + @Soffitcut.to_s + '|' + @Ridgecap_option.to_s + '|' + @Gutter_option.to_s + '" );'
				end

				@dlg002.execute_script(js_command)	
			}

			@dlg002.add_action_callback("PUSHTOHTML_ROOF_EDIT_GUTTER") {|action_context, params|

				if @Unitstemplate == "metric"

					@Guttervoffset_m = (@Guttervoffset*@in_mm).round(3)
					@Gutterext_m = (@Gutterext*@in_mm).round(3)
					@Dsplength_m = (@Dsplength*@in_mm).round(3)

					js_command = 'pushdata_gutter( "' + @Guttertype.to_s + '|' + @Guttervoffset_m.to_s + '|' + @Gutterext_m.to_s + '|' + @Downspoutoption.to_s + '|' + @Dsplength_m.to_s + '|' + @Dsptype.to_s + '|' + @Gutterwrapoption.to_s + '" );'
				else

					js_command = 'pushdata_gutter( "' + @Guttertype.to_s + '|' + @Guttervoffset.to_s + '|' + @Gutterext.to_s + '|' + @Downspoutoption.to_s + '|' + @Dsplength.to_s + '|' + @Dsptype.to_s + '|' + @Gutterwrapoption.to_s + '" );'
				end

				@dlg002.execute_script(js_command)	
			}


			@dlg002.add_action_callback("PUSHTOHTML_ROOF_EDIT_SOFFIT") {|action_context, params|

				if @Unitstemplate == "metric"

					@Ff_thk_m = (@Ff_thk*@in_mm).round(3)					
					@Ff_depth_m = (@Ff_depth*@in_mm).round(3)				
					@Soffit_thk_m = (@Soffit_thk*@in_mm).round(3)
					@Soffit_offset_m = (@Soffit_offset*@in_mm).round(3)
					@Soffit_ext_m = (@Soffit_ext*@in_mm).round(3)

					js_command = 'pushdata_soffit( "' + @Ff_option.to_s + '|' + @Ff_thk_m.to_s + '|' + @Ff_depth_m.to_s + '|' + @Fasciamat.to_s + '|' + @Soffit_option.to_s + '|' + @Soffit_type.to_s + '|' + @Soffit_thk_m.to_s + '|' + @Soffit_offset_m.to_s + '|' + @Soffit_ext_m.to_s + '|' + @Soffitmat.to_s + '" );'
				
				else

					js_command = 'pushdata_soffit( "' + @Ff_option.to_s + '|' + @Ff_thk.to_s + '|' + @Ff_depth.to_s + '|' + @Fasciamat.to_s + '|' + @Soffit_option.to_s + '|' + @Soffit_type.to_s + '|' + @Soffit_thk.to_s + '|' + @Soffit_offset.to_s + '|' + @Soffit_ext.to_s + '|' + @Soffitmat.to_s + '" );'
				
				end

				@dlg002.execute_script(js_command)	
			}

			@dlg002.add_action_callback("PUSHTOHTML_ROOF_EDIT_HR") {|action_context, params|

				if @Unitstemplate == "metric"

					@Hr_width_m = (@Hr_width*@in_mm).round(3)
					@Hr_thk_m = (@Hr_thk*@in_mm).round(3)
					
					js_command = 'pushdata_hr( "' + @Hr_width_m.to_s + '|' + @Hr_thk_m.to_s + '" );'
				else

					js_command = 'pushdata_hr( "' + @Hr_width.to_s + '|' + @Hr_thk.to_s + '" );'
				end

				@dlg002.execute_script(js_command)
			}



			#############################################

			
			@dlg002.add_action_callback("EXEC_ROOF_EDIT") {|action_context, params|

				if params
	
					MedeekMethods.main_menu_roof_edit
				end
			}

			@dlg002.add_action_callback("GET_ROOF_EDIT") {|action_context, params|
   		
				if params
					params = params.to_s
					plist = []
					plist = params.split("|")

					@Rooftype		= plist[0]

					@Pitch			= plist[1].to_f
					@Overhang		= plist[2].to_f
					@Birdcut 		= plist[3].to_f
					@Rd			= plist[4].to_f
					@Ply 			= plist[5].to_f
					@Rbd 			= plist[6].to_f
					@Rbw 			= plist[7].to_f
					@Hbd			= plist[8].to_f
					@Hbw 			= plist[9].to_f
					@Vbd 			= plist[10].to_f
					@Vbw	 		= plist[11].to_f
					@Rafter_spacing		= plist[12].to_f
					@Framingoption	 	= plist[13]
					@Advroofoptions	 	= plist[14]
					
					@Assy_name		= plist[15]


					if @Unitstemplate == "metric"

						@Overhang		= 	@Overhang * @mm_in
						@Birdcut		= 	@Birdcut * @mm_in
						@Rd			= 	@Rd * @mm_in
						@Ply			= 	@Ply * @mm_in
						@Rbd			= 	@Rbd * @mm_in
						@Rbw 			=	@Rbw * @mm_in
						@Hbd			= 	@Hbd * @mm_in
						@Hbw 			=	@Hbw * @mm_in
						@Vbd			= 	@Vbd * @mm_in
						@Vbw 			=	@Vbw * @mm_in
						@Rafter_spacing		= 	@Rafter_spacing * @mm_in
					end


					if @Advroofoptions == 'YES'
						js_command = 'callRubyADV( "' + 'Update' + '" );'
						@dlg002.execute_script(js_command)
					else
						MedeekMethods.main_menu_roof_edit
						js_command = 'callRubyexec( "' + 'Submit' + '" );'
						@dlg002.execute_script(js_command)
					end
					
				else
					raise "Options Input Cancelled"
				end
			
			}

			@dlg002.add_action_callback("GET_ROOF_EDIT_ADV") {|action_context, params|

				if params
					params = params.to_s
					plist = []
					plist = params.split("|")
						
					@Roofsheath_option		= 	plist[0]
					@Roofsheath_thk			= 	plist[1].to_f
					@Roofsheathmat			= 	plist[2]

					@Roofclad_option		= 	plist[3]
					@Roofclad_thk			= 	plist[4].to_f
					@Roofcladext			= 	plist[5].to_f
					@Roofcladmat			= 	plist[6]

					@Fascia_option			= 	plist[7]
					@Fascia_type			= 	plist[8]
					@Fascia_width			= 	plist[9].to_f
					@Fascia_depth			= 	plist[10].to_f
					@Soffitcut			= 	plist[11]

					@Ridgecap_option		= 	plist[12]
					@Gutter_option			= 	plist[13]
					
					if @Unitstemplate == "metric"
					
						@Roofsheath_thk		= 	@Roofsheath_thk * @mm_in
						@Roofclad_thk		= 	@Roofclad_thk * @mm_in
						@Roofcladext		= 	@Roofcladext * @mm_in
						@Fascia_width		= 	@Fascia_width * @mm_in
						@Fascia_depth		= 	@Fascia_depth * @mm_in
						
					end

					
					if @Fascia_option == 'YES'
						js_command = 'callRubySOF( "' + 'Update' + '" );'
						@dlg002.execute_script(js_command)
					else
						if @Gutter_option == 'YES'
							js_command = 'callRubyGUT( "' + 'Update' + '" );'
							@dlg002.execute_script(js_command)
						else
							if @Ridgecap_option == 'YES'
								js_command = 'callRubyHR( "' + 'Update' + '" );'
								@dlg002.execute_script(js_command)
							else
								MedeekMethods.main_menu_roof_edit
								js_command = 'callRubyexec( "' + 'Submit' + '" );'
								@dlg002.execute_script(js_command)
							end
						end
					end
					
				else
					raise "Options Input Cancelled"
				end
			}

			@dlg002.add_action_callback("GET_ROOF_EDIT_GUTTER") {|action_context, params|

				if params
					params = params.to_s
					plist = []
					plist = params.split("|")
					
					@Guttertype		= 	plist[0]
					@Guttervoffset		= 	plist[1].to_f
					@Gutterext		= 	plist[2].to_f
					@Downspoutoption	= 	plist[3]
					@Dsplength		= 	plist[4].to_f
					@Dsptype		= 	plist[5]
					@Gutterwrapoption	= 	plist[6]
					

					if @Unitstemplate == "metric"
					
						@Guttervoffset		= 	@Guttervoffset * @mm_in
						@Gutterext		= 	@Gutterext * @mm_in
						@Dsplength		= 	@Dsplength * @mm_in
	
					end
	
					if @Ridgecap_option == 'YES'
						js_command = 'callRubyHR( "' + 'Update' + '" );'
						@dlg002.execute_script(js_command)
					else
						MedeekMethods.main_menu_roof_edit
						js_command = 'callRubyexec( "' + 'Submit' + '" );'
						@dlg002.execute_script(js_command)
					end
					
				else
					raise "Options Input Cancelled"
				end
			}


			@dlg002.add_action_callback("GET_ROOF_EDIT_SOFFIT") {|action_context, params|

				if params
					params = params.to_s
					plist = []
					plist = params.split("|")
						
					@Ff_option		= 	plist[0]
					@Ff_thk			= 	plist[1].to_f
					@Ff_depth		= 	plist[2].to_f
					@Fasciamat		= 	plist[3]
					@Soffit_option		= 	plist[4]
					@Soffit_type		= 	plist[5]
					@Soffit_thk		= 	plist[6].to_f
					@Soffit_offset		= 	plist[7].to_f
					@Soffit_ext		= 	plist[8].to_f
					@Soffitmat		= 	plist[9]
					
					if @Unitstemplate == "metric"
					
						@Ff_thk			= 	@Ff_thk * @mm_in
						@Ff_depth		= 	@Ff_depth * @mm_in
						@Soffit_thk		= 	@Soffit_thk * @mm_in
						@Soffit_offset		= 	@Soffit_offset * @mm_in
						@Soffit_ext		= 	@Soffit_ext * @mm_in
					
					end
					
					if @Gutter_option == 'YES'
						js_command = 'callRubyGUT( "' + 'Update' + '" );'
						@dlg002.execute_script(js_command)
					else
						if @Ridgecap_option == 'YES'
							js_command = 'callRubyHR( "' + 'Update' + '" );'
							@dlg002.execute_script(js_command)
						else
							MedeekMethods.main_menu_roof_edit
							js_command = 'callRubyexec( "' + 'Submit' + '" );'
							@dlg002.execute_script(js_command)
						end
					end

				else
					raise "Options Input Cancelled"
				end
			}

			@dlg002.add_action_callback("GET_ROOF_EDIT_HR") {|action_context, params|

				if params
					params = params.to_s
					plist = []
					plist = params.split("|")
						
					@Hr_width		= 	plist[0].to_f
					@Hr_thk			= 	plist[1].to_f

					if @Unitstemplate == "metric"
					
						@Hr_width	= 	@Hr_width * @mm_in
						@Hr_thk		= 	@Hr_thk * @mm_in
						
					end

					MedeekMethods.main_menu_roof_edit
					js_command = 'callRubyexec( "' + 'Submit' + '" );'
					@dlg002.execute_script(js_command)
						
				else
					raise "Options Input Cancelled"
				end
			}


			
			@dlg002.add_action_callback("CLOSE_DIALOG") {|action_context, params|
				@dlg002.close
			}

			# Find and show our html file
	
			
			@dlg002.set_file File.dirname(__FILE__) + "/html/edit_complex_roof.html"
			@dlg002.show	


	
end

I don’t think the issue is related to the HTML dialog because I created a context function to regen the roof assembly which uses the same method as the edit function. It does not have any HTML dialog associated with it. The regen function is showing the same slowing of performance.

I’m at a loss to understand what is happening here.

I’ve tried the GC start command but it does not appear to have any effect.

I tried this module:

count = ObjectSpace.each_object(Numeric) {|x| p x }
puts "Total count: #{count}"

I get between about 100 to 250 objects, and it seems to fluctuate with each iteration, of hitting the edit or regen.

I’m doing a bunch of boolean subtraction in order to generate the roof primitive. I thought that might be the problem so I turned off that entire section of the code. Now the only geometry being created is a polgyon face within the overall group.

However, I’m still seeing a degradation in speed:

main menu roof edit triggered.
points collected 0.022001
roof extents generated 0.066004
geometry complete 0.071004
draw complete 0.590034

main menu roof edit triggered.
points collected 0.029002
roof extents generated 0.087005
geometry complete 0.091005
draw complete 0.636037

main menu roof edit triggered.
points collected 0.020002
roof extents generated 0.071005
geometry complete 0.076005
draw complete 0.657038

main menu roof edit triggered.
points collected 0.029002
roof extents generated 0.082005
geometry complete 0.087005
draw complete 0.702041

Without all of the complex boolean subtraction the degradation is not as pronounced but it is still there. This is all very strange. If I save the model, shut down SU and restart, open up the model and start with a regen performance is back to normal but after each successive edit or regen the performance time gradually falls apart.

It feels like I’m playing with some weird voodoo stuff here. Maybe it is time to reboot the computer, when all else fails this seems to fix a lot of ailments.

P.S. A nearby lightening strike must have hit the power lines, I got a hard reboot regardless when the power dropped for a few seconds. I really need to get a new UPS. Anyhow the reboot and restart of SketchUp was not the answer. I will keep working on this one.

This indicates it centers around the creation of geometry and the bloating of the undo stack.

Examine the difference between the two extensions and how they might use model.start_operation differently.

1 Like

Thank-you for the pointer. I think this may be the source of my frustration… I will keep you posted.

The funny thing is that the edit and regen functions neatly enclose everything within my start and commit operation methods so there is only one item added to the stack for every operation run, whereas my move edge tool has to toggle some layers on and off and does not as neatly handle the undo stack. Yet, it is the move edge tool that does not suffer from this performance degradation. I’m actually adding a couple more things to the stack with it.

It’s all very strange my move edge function starts with a tool which allows the user to select an edge they want to move it then sends this information back into the main body of my code which then updates the assembly (group), essentially regenerates it.

The methods called to regenerate the assembly are exactly the same as in my edit method, these four lines of code are exactly identical:

@group_outline = MedeekMethods.get_roof_extents @pts_aligned
MedeekMethods.create_complex_geometry
MedeekMethods.draw_selector_complex
@model0.commit_operation

So I don’t think it is anything I am doing specifically within these subroutines (methods) where I am calculating and creating the actual geometry of the roof.

I’m going to have to pull this whole thing apart until I’ve somehow narrowed down the root cause of this issue.

I’ve been thinking about this problem for two days now and my latest thought is the following:

When I check my CPU usage during the operation (when it is crunching for literally a minute or more) I noticed that the usage is almost pegged. This same thing happens when I quickly rotate the model around on my screen, SketchUp is having to do redraws and hence it takes computing power.

The only difference between the edit function and the move edge function is that the move edge function in instantiated via a SketchUp “tool”, once it completes the tool exits, it does not persist.

My theory is that this issue must have something to do with the fact that the edit function is not instantiated from a “tool” and for some reason it is trying to draw to the screen or something along those lines while it rebuilds/regenerates and this is causing the CPU spike and the delay. The same operations called from the move edge tool run within a second or less, the regen should run just as fast, it is essentially the same thing.

After comparing all of the methods used and pouring over my code with a fine tooth comb for more than 48 hours that is my best guess. However, I really have no idea how to fix it or even test my theory.

I’ve also come to the conclusion that the issue does not have to do with too many instance variables floating about. If that were the case then the move edge tool would exhibit the same behavior.