Le dimanche 07 décembre 2014 à 08:31 -0800, remi.ber...@gmail.com a écrit : > > > Hey guys, > > I'm currently playing with some Eratosthenes sieving in Julia, and > found a strange behavior of memory allocation. > My naive sieve is as follows: > > #= > Naive version of Erato sieve. > * bitarray to store primes > * only eliminate multiples of primes > * separately eliminate even non-primes > =# > function erato1(n::Int) > # Create bitarray to store primes > primes_mask = trues(n) > primes_mask[1] = false > > # Eliminate even numbers first > for i = 4:2:n > primes_mask[i] = false > end > > # Eliminate odd non-primes numbers > for i = 3:2:n > if primes_mask[i] > for j = (i + i):i:n > primes_mask[j] = false > end > end > end > > # Collect every primes in an array > n_primes = countnz(primes_mask) > primes_arr = Array(Int64, n_primes) > collect1!(primes_mask, primes_arr) > end > > > > With the collect1! function that takes a BitArray as argument and > return an Array containing the primes numbers. > > function collect1!(primes_mask::BitArray{1}, primes_arr::Array{Int64, > 1}) > prime_index = 1 > for i = 2:n > if primes_mask[i] > primes_arr[prime_index] = i > prime_index += 1 > end > end > return primes_arr > end > > > The codes works, but is slow because of a lot of memory allocation at > the line: > primes_arr[prime_index] = i > > > Here is an extract of the memory allocation profile > (--track-allocation=user): > > - function collect1!(primes_mask::BitArray{1}, > primes_arr::Array{Int64, 1}) > 0 prime_index = 1 > -84934576 for i = 2:n > 0 if primes_mask[i] > 184350208 primes_arr[prime_index] = i > 0 prime_index += 1 > - end > - end > 0 return primes_arr > - end > > > > > But, if I inline the definition of collect1! into the erato1, this is > much faster and the allocation in the loop of collect disapears. Here > is the code updated: > > function erato1(n::Int) > # Create bitarray to store primes > primes_mask = trues(n) > primes_mask[1] = false > > # Eliminate even numbers first > for i = 4:2:n > primes_mask[i] = false > end > > # Eliminate odd non-primes numbers > for i = 3:2:n > if primes_mask[i] > for j = (i + i):i:n > primes_mask[j] = false > end > end > end > > # Collect every primes in an array > n_primes = countnz(primes_mask) > primes_arr = Array(Int64, n_primes) > prime_index = 1 > for i = 2:n > if primes_mask[i] > primes_arr[prime_index] = i > prime_index += 1 > end > end > return primes_arr > end > > > And the memory profile seems more reasonable: > > 0 n_primes = countnz(primes_mask) > 92183392 primes_arr = Array(Int64, n_primes) > 0 prime_index = 1 > 0 for i = 2:n > 0 if primes_mask[i] > 0 primes_arr[prime_index] = i > 0 prime_index += 1 > - end > - end > > > > So I'm wondering why the simple fact of inlining the code would remove > the massive memory allocation when assigning to the array. I think the difference may come from the fact that n is not a parameter of collect1!(). As a general rule, avoid using global variables (or at least mark them as const). Anyway it's much clearer when a function only depends on its arguments, else it's very hard to follow what it does.
In the specific case of your example, Julia looks for n in the global scope, not even in the parent function erato1(). So with a fresh session, I get: julia> erato1(100) ERROR: n not defined in collect1! at none:3 in erato1 at none:23 I'm not clear on how variable scoping works with function calls: the manual says variables are inherited by inner scopes. But I guess it does not apply to calling a function, only to defining it. Is that right? I could add a word to the manual if it's deemed useful. http://docs.julialang.org/en/latest/manual/variables-and-scoping/ Regards