Почему мой код julia работает так медленно?

redim = 2;
# Loading data
iris_data = readdlm("iris_data.csv");
iris_target = readdlm("iris_target.csv");

# Center data
iris_data = broadcast(-, iris_data, mean(iris_data, 1));
n_data, n_dim = size(iris_data);

Sw = zeros(n_dim, n_dim);
Sb = zeros(n_dim, n_dim);

C = cov(iris_data);


classes = unique(iris_target);

for i=1:length(classes)
    index = find(x -> x==classes[i], iris_target);
    d = iris_data[index,:];
    classcov = cov(d);
    Sw += length(index) / n_data .* classcov;
end
Sb = C - Sw;

evals, evecs = eig(Sw, Sb);
w = evecs[:,1:redim];
new_data = iris_data * w;

Этот код просто выполняет LDA (линейный дискриминантный анализ) для iris_data. Уменьшите размеры iris_data до 2. Это займет около 4 секунд, но Python (numpy/scipy) займет всего около 0,6 секунды. Почему?


person Hewen Xu    schedule 05.01.2016    source источник
comment
стоит прочитать советы по повышению эффективности.   -  person daycaster    schedule 05.01.2016
comment
Вам не нужны все эти точки с запятой ; в Джулии.   -  person HarmonicaMuse    schedule 05.01.2016


Ответы (1)


Это первая страница, второй абзац введения в Руководстве Джулии:

Поскольку компилятор Джулии отличается от интерпретаторов, используемых для таких языков, как Python или R, вы можете обнаружить, что производительность Джулии поначалу не интуитивно понятна. Если вы обнаружите, что что-то работает медленно, мы настоятельно рекомендуем прочитать Советы по повышению производительности, прежде чем пробовать что-либо еще. Как только вы поймете, как работает Julia, вам будет легко писать код, который почти так же быстр, как C.


Выдержка:

Избегайте глобальных переменных

Значение глобальной переменной и, следовательно, ее тип могут измениться в любой момент. Это затрудняет компилятору оптимизацию кода с использованием глобальных переменных. Переменные должны быть локальными или по возможности передаваться функциям в качестве аргументов.

Любой код, критически важный для производительности или тестируемый, должен находиться внутри функции.

Мы обнаружили, что глобальные имена часто являются константами, и объявление их как таковых значительно повышает производительность.


Зная, что стиль скрипт (весь процедурный код верхнего уровня) настолько распространен среди многих пользователей научных вычислений, я бы порекомендовал вам, по крайней мере, обернуть весь файл в выражение let для начала (позвольте представить новый локальная область), то есть:

let

redim = 2
# Loading data
iris_data = readdlm("iris_data.csv")
iris_target = readdlm("iris_target.csv")

# Center data
iris_data = broadcast(-, iris_data, mean(iris_data, 1))
n_data, n_dim = size(iris_data)

Sw = zeros(n_dim, n_dim)
Sb = zeros(n_dim, n_dim)

C = cov(iris_data)


classes = unique(iris_target)

for i=1:length(classes)
    index = find(x -> x==classes[i], iris_target)
    d = iris_data[index,:]
    classcov = cov(d)
    Sw += length(index) / n_data .* classcov
end
Sb = C - Sw

evals, evecs = eig(Sw, Sb)
w = evecs[:,1:redim]
new_data = iris_data * w

end

Но я бы также призвал вас реорганизовать это в небольшие функции, а затем составить функцию main, которая вызывает остальные, что-то вроде этого, обратите внимание, как этот рефакторинг делает ваш код общим и повторно используемым (и быстрым):

module LinearDiscriminantAnalysis

export load_data, center_data

"Returns data and target Matrices."
load_data(data_path, target_path) = (readdlm(data_path), readdlm(target_path))

function center_data(data, target)
    data = broadcast(-, data, mean(data, 1))
    n_data, n_dim = size(data)
    Sw = zeros(n_dim, n_dim)
    Sb = zeros(n_dim, n_dim)
    C = cov(data)
    classes = unique(target)
    for i=1:length(classes)
        index = find(x -> x==classes[i], target)
        d = data[index,:]
        classcov = cov(d)
        Sw += length(index) / n_data .* classcov
    end
    Sb = C - Sw
    evals, evecs = eig(Sw, Sb)
    redim = 2
    w = evecs[:,1:redim]
    return data * w
end

end

using LinearDiscriminantAnalysis

function main()
    iris_data, iris_target = load_data("iris_data.csv", "iris_target.csv")
    result = center_data(iris_data, iris_target)
    @show result
end

main()

Примечания:

  • Вам не нужны все эти точки с запятой.
  • анонимные функции в настоящее время работают медленно, но это изменится в v0.5. Вы можете пока использовать FastAnonymous, если производительность критична.
  • В общем, внимательно прочитайте и примите во внимание все советы по повышению производительности.
  • main это просто имя, это может быть что угодно.
person HarmonicaMuse    schedule 05.01.2016
comment
есть ли разница между использованием функции main и использованием блока let, или это просто лучшая практика? как насчет объявления некоторых переменных как const, например iris_data? Я часто обнаруживаю, что объявляю данные, которые я хочу вычислить, как const, учитывая, что стиль скрипта настолько распространен среди многих пользователей научных вычислений: P - person amrods; 05.01.2016
comment
Большое спасибо! - person Hewen Xu; 06.01.2016
comment
Разница в том, что это действительно лучшая практика! :D Объявление глобальных переменных как const также будет работать, но тогда вы потеряете модульность (полезно для тестирования, профилирования и сравнительного анализа, не впадая в смешение) использования модулей и функций для повторного использования кода с n разными наборами данных, функциональность center_data не работает и должна не зависят от iris_data.csv и iris_target.csv, например, это просто параметры, переданные в функцию, что делает ее многоразовой, а не одноразовой. - person HarmonicaMuse; 06.01.2016