Notifications
Clear all

[Closed] memory issue getelementsusingface

Hi,

i’m working on a script which detaches elements in a poly to separate objects. I’ve got an issue with the part of the script where I collect all elements in the poly. It has to be called quite often but it uses too much memory. Is there any way to improve this? I suspect getElementsUsingFace is the problem but have no Idea to improve it.

fn firstBit arr = 
 (
 	 --get's first bit in a bitarray. Method by denisT
 	 local b
 	 for n in arr while
 	 (
 		 b = n
 		 false
 	 ) do ()
 	 b
  )
  
  fn test theNode = 
  (
  	local polyop_getElementsUsingFace = polyop.getElementsUsingFace
  	local numfaces = polyop.getNumFaces theNode
  	local faceList = #{1..numfaces} --initialize the facelist
  	local arrElementFaceIndices = #() --an array of bitarrays. Each bitarray represents faces of a single element
  
  	--get all elements
  	while facelist.numberset > 0 do
  	(
  		local elementFaces = polyop_getElementsUsingFace theNode (firstBit faceList)
  		append arrElementFaceIndices elementFaces
  		facelist -= elementFaces
  	)
  )

Now create a poly with some elements and run the test. For example

a = for i = 1 to 100 collect sphere radius:10 pos:(random [0,0,0] [300,300,300])
  for i = a.count to 2 by -1 do (a[1] + a[i];delete a[i])
  converttopoly a[1]
 clearListener()
 gc()
 st = timeStamp()
 mem = heapFree
 for i = 1 to 10 do 
 (
 	test a[1]
 )
 format "Time: % ms, memory: %
" (timestamp()-st) (mem-heapfree) 
 
28 Replies

Hi Klaas,
I don’t get any memory issues here.

Time: 60 ms, memory: 184560L

i don’t see any reason for ‘huge’ memory leaking there too. getelementsusingface is slow but is not really ‘memory expensive’.

ps. for a goal of ‘super’ optimization is better to use bitarray.isempty instead of bitarray.numberset. the first one is faster and doesn’t leak

Thanks guys for the feedback,

maybe the leak is somewhere else in the method or it is inevitable. I’ll post the entire method here, maybe you see an issue elsewhere.
When I run this, I lose about 1 GB of memory on the detaching process.

fn firstBit arr = 
  (
  	local b
  	for n in arr while
  	(
  		b = n
  		false
  	) do ()
  	b
  )
  
  fn collapsePoly arrObject =
  (
  	disableSceneRedraw()
  	setCommandPanelTaskMode #create
  	undo off while (arrObject.count > 1) do for i = arrObject.count to 2 by -2 do 
  	(
  		arrObject[i].EditablePoly.attach arrObject[i-1] arrObject[i]
  		deleteItem arrObject (i-1)
  	)
  	enableSceneRedraw()
  	completeRedraw()
  	arrObject[1]
  )
  
  function fn_detachElementsByDeleting arrObject &collection theName:"Klaas"= 
  (
  	/*<FUNCTION>
  	Description
  		detaches elements as separate objects from a poly, retains object name, material and uv coordinates
  		splits the object in half recursively until elements can't be detached anymore
  	Arguments
  		<array> arrObject: the array of objects we're splitting
  		<array by reference> collection: if an object can't be split anymore, add it to this array.
  	Return
  	<FUNCTION>*/
  	
  	--store some methods
  	local polyop_getElementsUsingFace = polyop.getElementsUsingFace
  	local polyop_deleteFaces = polyop.deleteFaces
  	setCommandPanelTaskMode #create
  	
  	with undo off with redraw off
  	(
  		for theNode in arrObject where iskindof theNode Editable_Poly do
  		(
  			local numfaces = polyop.getNumFaces theNode
  			local faceList = #{1..numfaces} --initialize the facelist
  			local arrElementFaceIndices = #() --an array of bitarrays. Each bitarray represents faces of a single element
  			
  			--get all elements, biggest memory issue arises here
  			while NOT facelist.isEmpty do
  			(
  				local elementFaces = polyop_getElementsUsingFace theNode (firstBit faceList)
  				append arrElementFaceIndices elementFaces
  				facelist -= elementFaces
  			)
  			
  			if arrElementFaceIndices.count > 1 then --if the object has more than one element we want to split it
  			(
  				--get half of the elements
  				local halfOfTheElements = #{1..numfaces}
  				for n = 1 to arrElementFaceIndices.count by 2 do halfOfTheElements -= arrElementFaceIndices[n]
  				
  				--create two halves from one object
  				local theHalf = copy theNode
  				polyop_deleteFaces theHalf -halfOfTheElements delIsoVerts:true --delete unneeded faces from theHalf		
  				polyop_deleteFaces theNode halfOfTheElements delIsoVerts:true --delete the processed faces from the original object
  					
  				--set some properties
  				theHalf.name = theName--theNode.name
  				theHalf.wirecolor = random blue yellow
  				theNode.layer.addNode theHalf
  				
  				--recurse
  				fn_detachElementsByDeleting #(theNode,theHalf) &collection theName:theName
  				
  			)else --if there's one element in the object, collect it
  			(
  				append collection theNode
  			)
  		)
  	)
  )
  
  --set up some test objects
  delete objects
  a = for i = 1 to 100 collect convertToPoly (teapot radius:10 segments:12 pos:(random [0,0,0] [300,300,300]))
  thePoly = collapsePoly a
  
  clearListener()
  gc()
  st = timeStamp()
  mem = heapFree
  collection = #()
  
  fn_detachElementsByDeleting #(thePoly) &collection theName:thePoly.name
  
  format "Time: % ms, memory: %
" (timestamp()-st) (mem-heapfree) 
  

how big is your polyobject (num faces, num elements)?

I’ve tested on an object of about 400 elements with in total 1 mln triangles. Elements varied in size. If you use the test-script in my second post here with the teapots you should get a similar object.
The memory increases when elements have more faces, which sounds understandable.

Yes, there is definitely some accumulative big leaking there, not even being removed by gc().
It must be in the method to split the poly I guess.

A quick workaround could be to converttopoly the resulting array and doing a manual cg(). This will free up around 1GB of RAM for the testing scene.

fn_detachElementsByDeleting #(thePoly) &collection theName:thePoly.name
  convertToPoly collection
  gc()

Actually early convertion to poly would be better, and it’s not much slower. Also no need for gc().

)else --if there's one element in the object, collect it
 (
 	converttopoly theNode
 	append collection theNode
 )

here is a test scene:

fn fastAttach nodes =
 (
 	k = 1
 	attach = polyop.attach
 	while nodes.count > 1 do
 	(
 		k += 1
 		attach nodes[k-1] nodes[k]
 		deleteItem nodes k
 		if k >= nodes.count do k = 1
 	)
 	nodes[1]
 )
 
 gc()
 max create mode
 delete objects
 (
 	count = 50
 	seed 0
 	node = fastAttach \
 	(
 		for k=1 to count collect 
 		(
 			converttopoly (teapot radius:(random 1 10) segments:(random 10 24) pos:(random -[20,20,20] [20,20,20]))
 		)
 	)
 	numfaces = node.numfaces
 	
 	format "detaching... %
" numfaces
 )

how long does it take do split the node on its elements using your algorithm?

@DenisT: Between 5.4 and 5.5 seconds for the 900k triangles generated by your code.
The trick by polytools3d to use the converttopoly before adding the node to the array also worked wonders in reclaiming the memory. Thanks a lot for that.

2 Replies
(@denist)
Joined: 1 year ago

Posts: 0

5.4-5.5 seconds sound too good. could you post a detaching code please? you are probably missing something or your machine is much faster than everyone’s else.

(@polytools3d)
Joined: 1 year ago

Posts: 0

Yes, it works that fast, and works well, at least with the test scenes posted here.

Result for @DenisT test scene (50 Teapots / 900K Tris / 200 Elements)
Time: 4662 ms, memory: 613944L

Great code Klaas!

Page 1 / 3