The following code will use no more than 20 threads at a time process 50k items in an array, but the used heap on a 64 bit Windows machine jumps from 54 Megs to 2.9 Gigs! A GC will clear out the heap, but that is just way too much memory.
Not using parallel runs in 312 ms but adding the threading slows the request down to 15 seconds. The FusionReactor profiler shows that nearly 80% of the request was spent cloning the page context.
A heap dump of the server's memory also shows that there are a huge number of page context objects in the heap which were expensive to create.
Lucee appears to create 50,000 new page contexts which is very wasteful. Since there are no more than 20 threads running at a time, no more than 20 page contexts should have been created and pooled.
I actually don't think Lucee should be cloning the pc at all! Unlike a cfthread which can be a daemon, an arrayEach() or structEach() thread by design will never outlive the parent request and runs "inside" of the parent request. Therefore, why are we cloning the pc at all? This is just wasted resources and time. The threads should be able to share the same pc in this instance.
I've run into this and had it cause out of memory errors due to wasted memory and I also have clients running into this consuming high amounts of memory on their servers while trying to process large batches of items using arrayEach() or queryEach().
Hi - Brad enlightened me that it’s not just ++. It’s not just a question of writing that longhand. Many operations are not thread safe and the example of locking that he gave is probably what should go in the docs at least until Lucee can do that behind the scenes.
we got memory usage down and also the time spend, i see now the same numbers for both cases BUT in contrast to for example a simple for loop, it is still much slower! so i created an other ticket for this addressing overall performance of this
Memory usage on arrayEach is still quite high compared to a for loop in the 5.3.7 RC. We have a couple of CPU- and memory-intensive tasks that loop over 2,000+ structs. If we use .each(), we’ve seen memory usage go from 4 GB to 24 GB. With for loops doing exactly the same thing, we haven’t seen anything like that.
the following change has been made to 188.8.131.52 and 184.108.40.206 which reduces the memory usage drastically. previously it was creating a pageContext for each array element, which exploded memory usage for large arrays