Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Array#push causes "stack level too deep" error with large arrays

Tags:

I made two arrays, each with 1 million items:

a1 = 1_000_000.times.to_a a2 = a1.clone 

I tried to push a2 into a1:

a1.push *a2 

This returns SystemStackError: stack level too deep.

However, when I try with concat, I don't get the error:

a1.concat a2 a1.length # => 2_000_000 

I also don't get the error with the splat operator:

a3 = [*a1, *a2] a3.length # => 2_000_000 

Why is this the case? I looked at the documentation for Array#push, and it is written in C. I suspect that it may be doing some recursion under the hood, and that's why it's causing this error for large arrays. Is this correct? Is it not a good idea to use push for large arrays?

like image 999
Kapil Dewade Avatar asked Aug 23 '18 23:08

Kapil Dewade


People also ask

What do you mean by array?

An array is a data structure, which can store a fixed-size collection of elements of the same data type. An array is used to store a collection of data, but it is often more useful to think of an array as a collection of variables of the same type.

What is an array with example?

An array is a collection of similar types of data. For example, if we want to store the names of 100 people then we can create an array of the string type that can store 100 names. String[] array = new String[100];

What are the 3 types of arrays?

There are three different kinds of arrays: indexed arrays, multidimensional arrays, and associative arrays.

What is array and its type?

An array type is a user-defined data type consisting of an ordered set of elements of a single data type. An ordinary array type has a defined upper bound on the number of elements and uses the ordinal position as the array index.


1 Answers

I think that this is not a recursion error, but an argument stack error. You are running up against the limit of the Ruby VM stack depth for arguments.

The problem is the splat operator, which is passed as an argument to push. The splat operator is expanded into a million element argument list for push.

As function arguments are passed as stack elements, and the pre-configured max size of the Ruby VM stack size is:

RubyVM::DEFAULT_PARAMS[:thread_vm_stack_size] => 1048576 

..this is where the limit comes from.

You can try the following:

RUBY_THREAD_VM_STACK_SIZE=10000000 ruby array_script.rb 

..and it will work fine.

This is also the reason you want to use concat instead, as the whole array can be passed as one single reference, and concat will then process the array internally. As opposed to push + splat, which will try to use the stack as a temporary storage for all the array elements.

like image 100
Casper Avatar answered Nov 08 '22 22:11

Casper