[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)
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)
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.
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.
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!