Notifications
Clear all

[Closed] Struct creation leaks

here is another example…


(
	struct a(p)
	
	t0 = timestamp()
	global h0 = heapfree
	format "0	> free:% 	size:%
" heapfree heapsize
	
	gc()
	for k=1 to 20000000 do
	(
		p = a p:z_axis
		collectvalue &p 
	)
	
	t1 = timestamp()
	h1 = heapfree
	format "	> free:% 	size:% 	time:% 	heap:%
" heapfree heapsize (t1-t0) (h0 - h1)

	global t2 = timestamp()
	global h2 = heapfree
	gc()
	format "0	> free:% 	size:% 	time:% 		heap:%(%)
" heapfree heapsize (t2-t1) (h0 - h2) (h1 - h2) 
)
(
	t3 = timestamp()
	h3 = heapfree
	gc()

	format "0	> free:% 	size:% 	time:% 	heap:%(%)
" heapfree heapsize (t3-t2) (h0 - h3) (h2 - h3) 
)



the numbers can be different, because it depends on your machine configuration.

but what we can see:

#1 the system accumulates memory used in local (loop scope)
#2 be able to complete the task the system increases Heap Size (which will be never returned back)
#3 gc() doesn’t work in local scope

what is happening if we don’t have enough virtual memory for our Heap? The system probably will use hard drive to swap

so after increasing the Heap the system will always try to use whole available Heap and swap on hard if it’s not enough.

am i wrong?

Honestly I don’t really care… I’ve rewrote array, map iterator, arrays of float, point2(3,4) , matrix3…

They are not leaking … (oops … sorry if i use the wrong term) … anymore .

I’ve showed a lot of examples where I think the system leaks. No! I had an answer that the leaking is more like a feature.

But at 2017 I hear that all years before everything was wrong. But NOW all is much better excepting not really significant issue where a BitArray iteration in some cases is hundred times slower.

Larry,

there are a lot of your source code in max sdk. it’s the only real resource for me to learn and understand how some things work…

fine… please show me the code behide:

free <matrix3>

thanks

1 Reply
(@lminton)
Joined: 11 months ago

Posts: 0

There is no implementation of free for Matrix3Value. free is implemented for classes like array, string, stringstream where the class holds malloc’ed memory, and frees that malloc’ed memory. Matrix3Value simply contains a Matrix3 member value, so there is nothing to free.

Without knowing your implementation collectvalue, I can’t say how robust it is. I can think of various potential gotcha’s – a pointer to the last value allocated in heap is stored in thread local storage and is used in garbage collection, various value types are stored in caches and collecting a value without removing it from the cache will result is failures.

Larry

oh… i forget to tell.

avg_dlx.cpp is ~9K lines of code.
dts_script_extension.cpp is ~41K

Maxscript uses mark and sweep garbage collection. It would be nice if it did reference counting garbage collection (what you want), but it simply doesn’t.

In 2016 my results are:
count:10000 = time:7 heap:120L
count:100000 = time:118 heap:1050544L
count:10000000 = time:16582 heap:1742080L
-count:10000000 = time:7993 heap:128L

in 2017 my results are:
count:10000 = time:10 heap:136L
count:100000 = time:103 heap:136L
count:10000000 = time:10322 heap:136L
-count:10000000 = time:12338 heap:136L

From the numbers, it looks like in 2016 that the result of a for loop expression was needlessly being migrated from stack to heap. It no longer does so in 2017.

Larry

First of all I want to thank you, Larry, for you answers and ignoring my harsh manners
But believe me I’m spending a lot for time testing and debugging my tools before I deliver them to the client.

Garbage Collection mechanism is an absolute Black Box. There is no any documentation about it. I spent probably months to find good and safe solution.

Well. Let’s back to some areas that still not clear for me…

As I can see max 2017 solved the issue with Point2(3,4) and Matrix3 values in local (loop) scope. But the same issue with Arrays, Structs, and BitArrays still exists (and it became worse in some situations).

My another question was about ‘free’ function. Now I see that is kind of ‘dummy’ operation which magically effects memory usage (freeing)
It’s just doing the same as OK:

(
	t = timestamp()
	h = heapfree

	for k=1 to 1000000 do
	(
		tm0 = matrix3 0
		--ok
 	)

	format "time:% heap:%
" (timestamp() - t) (h - heapfree)
)

with no OK:
time:536 heap:101952416L

with OK:
time:528 heap:120L

It’s a very serious issue. For example in a SimpleMod where you constantly map through all points with no chance to do GC. If you have small size of heapsize you will meet ‘lagging’ often, if you have big heapsize the ‘lags’ will be longer.
The similar situation with a SimpleObject.

I think that the memory (garbage collection) issue is the most critical today. The MXS works pretty well for tools development, but memory … not optimal usage … nullifies any efforts to handle complex meshes or structures.

i think we have to change maxscript coding rules…
for example:
#1

(
	(
		local a = <value>
	)
	print a
)

this is wrong… out of scope where a was declared it should undefined
… and it should be collected

that’s what i exactly do.
but… look at another situation:
#2

(
	struct s(a)
	c = s()
	(
		local a = <value>
		c.a = a
	)
	print c.a
)

i can collect a, but it will be unsafe because it’s used in the upper level c
i give an option in this case:
#1 check if a is taken on upper level
#2 do collect unsafe

#1 takes a time. probably mostly because i do too much verifications
#2 unsafe. it tells all about itself

so my idea is –
a developer should know what variables have to be (or can be) collected (without verification it takes
no time)

which means the example #1 might be

(
	(
		local a = <value>
                collectvalue &a
	)
	print a
)

and #2

(
	struct s(a)
	c = s()
	(
		local a = <value>
		c.a = a
	)
        collectvalue &c
	print c.a
)

it’s more like the rule about ‘construction and destruction’ or ‘creation and destroying’.

i would like to call it ‘free’ instead of ‘‘collect’, but the name ‘free’ is already taken

For the example above, I get:

time:530 heap:132L
time:550 heap:132L

I suspect that at some point you ended up declaring tm0 in the global scope. Otherwise the matrix3 instances will just exist on the stack and not consume heap memory.

The difference in behavior you see with Arrays, Structs, and BitArrays is that they cannot be allocated on the stack, they have to be allocated in heap. The reason for this is that they have a dtor that must be called to call ‘free’ on malloc’ed memory. Since dtors are not called on values allocated on the stack, they need to be in heap.

Larry

with no OK:
time:536 heap:101952416L

with OK:
time:528 heap:120L

these numbers are for max 2014 and 2016. For max 2017 this particular issue was fixed.

Page 3 / 3