Minimizing memory usage in Julia function

Viewed 369

This function is a workhorse which I want to optimize. Any idea on how its memory usage can be limited would be great.

function F(len, rNo, n, ratio = 0.5)
    s = zeros(len);  m = copy(s); d = copy(s);
    s[rNo]=1 

    rNo ≤ len-1 && (m[rNo + 1] = s[rNo+1] = -n[rNo])
    rNo > 1 && (m[rNo - 1] = s[rowNo-1] = n[rowNo-1])

    r=1 
    while true
        for i ∈ 2:len-1
            d[i] = (n[i]*m[i+1] - n[i-1]*m[i-1])/(r+1)
        end 

        d[1] = n[1]*m[2]/(r+1); 
        d[len] = -n[len-1]*m[len-1]/(r+1); 

        for i ∈ 1:len
            s[i]+=d[i]
        end 

        sum(abs.(d))/sum(abs.(m)) < ratio && break #converged
                                                                                               
        m = copy(d); r+=1 
    end 

    return reshape(s, 1, :)
end

It calculates rows of a special matrix exponential which I stack later.

Although the full method is quite faster than built in exp thanks to the special properties, it takes up far more memory as measured by @time.

Since I am a noob in memory management and also in Julia, I am sure it can be optimized quite a bit..

Am I doing something obviously wrong?

1 Answers

I think most of your allocations come from sum(abs.(d))/sum(abs.(m)) < ratio && break #converged. If you replace it with sum(abs, d)/sum(abs,m) < ratio && break #converged those allocations should go away. (it also will be a speed boost).

Your other allocations can be removed by replacing m = copy(d) with m .= d which does an element-wise copy.

There are also a couple of style things where I think you could make this a nicer function to read and use. My changes would be as follows

function F(rNo, v, ratio = 0.5)
    len = length(v)
    s = zeros(len+1);  m = copy(s); d = copy(s);
    s[rNo]=1 

    rNo ≤ len && (m[rNo + 1] = s[rNo+1] = -v[rNo])
    rNo > 1 && (m[rNo - 1] = s[rowNo-1] = v[rowNo-1])

    r=1 
    while true
        for i ∈ 2:len
            d[i] = (v[i]*m[i+1] - v[i-1]*m[i-1]) / (r+1)
        end 

        d[1] = v[1]*m[2]/(r+1); 
        d[end] = -v[end]*m[end]/(r+1); 

        s .+= d

        sum(abs, d)/sum(abs, m) < ratio && break #converged
                                                                                               
        m .= d; r+=1 
    end 

    return reshape(s, 1, :)
end

The most notable change is removing len from the arguments. Including an array length argument is common in C (and probably others) where finding the length of an array is hard, but in Julia length is cheap (O(1)), and adding extra arguments is just more clutter and confusion for the people using it. I also made use of the fact that julia is able to turn s[end] into s[length(x)] to make this a little cleaner. Also, in general when using Julia you should look for ways to use dotted operations rather than writing for loops. The for loops will be fast, but why take 3 lines to do what you could in 1 shorter line? (I also renamed n to v since to me n is a number and v is a vector, but that is pure preference).

I hope this helps.

Related