Le dimanche 07 décembre 2014 à 09:12 -0800, John Myles White a écrit : > It might be useful to put a bit about Julia being lexically scoped > into the manual and refer to the Wikipedia article on scope. Ah, that's the term I needed. Opened a pull request here: https://github.com/JuliaLang/julia/pull/9267
Regards > — John > > > On Dec 7, 2014, at 9:05 AM, Milan Bouchet-Valat <nalimi...@club.fr> wrote: > > > > 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 > >