Медленнее смешивать логические переменные с двойным?

У меня есть 0-1-значные векторы, для которых мне нужно выполнить некоторые операции с матрицей. Они не очень разрежены (только половина значений равна 0), но сохранение их как логической переменной вместо двойной сохраняет в 8 раз больше памяти: 1 байт для логического и 8 для двойной с плавающей запятой.

Было бы все медленнее делать матричные умножения логического вектора и двойной матрицы, чем использовать их как двойные? Ниже приведены мои предварительные результаты:

>> x = [0 1 0 1 0 1 0 1]; A = rand(numel(x)); xl = logical(x);
>> tic; for k = 1:10000; x * A * x'; end; toc %'
Elapsed time is 0.017682 seconds.
>> tic; for k = 1:10000; xl * A * xl'; end; toc %'
Elapsed time is 0.026810 seconds.
>> xs = sparse(x);
>> tic; for k = 1:10000; xs * A * xs'; end; toc %'
Elapsed time is 0.039566 seconds.

Кажется, что использование логического представления намного медленнее (а разреженный еще медленнее). Может кто-нибудь объяснить, почему? Это время литья типа? Является ли это ограничением набора команд CPU/FPU?

EDIT: Моя система - MATLAB R2012b в Mac OS X 10.8.3, Intel Core i7 3,4 ГГц

EDIT2: Несколько комментариев показывают, что это только проблема с Mac OS X. Я хотел бы скомпилировать результаты из различных архитектур и ОС, если это возможно.

EDIT3: Моя фактическая проблема требует вычисления с огромной частью всех возможных двоичных векторов длины m, где m может быть слишком большим для 8 * m * 2^m для размещения в памяти.

Ответ 1

Начну с публикации немного лучшего теста. Я использую функцию TIMEIT от Стив Эддинс, чтобы получить более точные тайминги:

function [t,err] = test_mat_mult()
    %# data
    N = 4000; sparsity = 0.7;    %# adjust size and sparsity of data
    x = double(rand(1,N) > sparsity);
    xl = logical(x);
    xs = sparse(x);
    A = randn(N);

    %# functions
    f = cell(3,1);
    f{1} = @() mult_func(x,A);
    f{2} = @() mult_func(xl,A);
    f{3} = @() mult_func(xs,A);

    %# timeit
    t = cellfun(@timeit, f);

    %# check results
    v = cellfun(@feval, f, 'UniformOutput',true);
    err = max(abs(v-mean(v)));  %# maximum error
end

function v = mult_func(x,A)
    v = x * A * x';
end

Ниже приведены результаты на моей машине (WinXP 32-бит, R2013a) с N = 4000 и разрешающей способностью = 0,7:

>> [t,err] = test_mat_mult
t =
     0.031212    %# double
     0.031970    %# logical
     0.071998    %# sparse
err =
   7.9581e-13

Вы можете видеть, что double в среднем немного лучше, чем logical, тогда как sparse медленнее, чем ожидалось (поскольку его фокус - эффективное использование памяти, а не скорость).


Теперь обратите внимание, что MATLAB полагается на реализации BLAS, оптимизированных для вашей платформы, для выполнения полное матричное умножение (подумайте DGEMM). В общем случае это включает в себя процедуры для одиночных/двойных типов, но не логические, поэтому произойдет преобразование, которое объясняет, почему его медленнее для logical.

В процессорах Intel процедуры BLAS/LAPACK предоставляются Intel MKL Library. Не уверен в AMD, но я думаю, что он использует эквивалентный ACML:

>> internal.matlab.language.versionPlugins.blas
ans =
Intel(R) Math Kernel Library Version 10.3.11 Product Build 20120606 for 32-bit applications

Конечно, редкий случай - это совсем другая история. (Я знаю, что MATLAB использует пакет SuiteSparse для многих своих разреженных операций, но я не уверен).

Ответ 2

Я думаю, что результаты разумно связаны с различными представлениями.

Неразрешимый двойной массив прост и эффективен для представления небольшого массива данных, который очень легко вписывается в кеш.

Логический массив более эффективен с точки зрения пространства, используя только байты на элемент вместо 8 байтов, но ничего не получает, когда у вас есть только 8 элементов. С другой стороны, его нужно преобразовать в double, прежде чем использовать двойную арифметику, добавив шаг.

В разреженном массиве используется более сложное представление, предназначенное для экономии места, когда большая часть массива равна нулю. Для этого требуется больше операций либо решить, что элемент с заданным индексом равен нулю, либо получить его ненулевое значение. Использование его для 50% ненулевого массива, который легко вписывается в даже самые маленькие кеши, является неправильным использованием. Это в лучшем случае уменьшает стоимость памяти и передачи данных большого массива, который почти равен нулю. См. Редкий против нормального массива Matlab

Если вы действительно имеете дело с 8 массивами элементов, вы должны придерживаться не разреженных массивов двойных. Если ваша настоящая работа связана с более крупными массивами, вам необходимо сравнить такие же размеры. Вам также необходимо убедиться, что разреженность ваших тестовых данных соответствует реальным данным.

Ответ 3

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

Доступ к данным из кеша L1 достаточно быстр, чтобы быть "эффективно свободным", когда на каждый загруженный элемент данных выполняется достаточный объем вычислительной работы (как в случае вашего примера). Когда это происходит, скорость выполнения ограничена вычислением, а не загрузкой/хранением трафика; используя логические переменные, вы делаете больше вычислений, что замедляет ваш тест.

Насколько велик рабочий набор проблемы, который вы действительно хотите решить? Если он по крайней мере больше, чем кэш L2 на вашем процессоре, вы должны просто использовать обычные двойные матрицы. Точный порог, при котором использование логических переменных становится выгодным, вероятно, значительно больше, но для этого потребуется определенный эксперимент. (Это также будет зависеть от того, как именно MATLAB обрабатывает преобразование, вы хотите сделать преобразование как часть тайлинга для умножений - если MATLAB этого не сделает, он, вероятно, никогда не будет быстрее, чем с помощью double, no как большой набор данных).