Ответ 1
EDIT: ответ изменен в соответствии с предложениями Олега (см. комментарии).
Вот мой ориентир для второй части вашего вопроса. Для тестирования прямой вставки матрицы инициализируются пустым с изменением nzmax
. Для тестирования перестройки из индексных векторов это не имеет значения, так как матрица создается с нуля при каждом вызове. Эти два метода были протестированы для выполнения одной операции вставки (различного количества элементов) или для инкрементных вставок, по одному значению за раз (до одного и того же количества элементов). Из-за вычислительной деформации я снизил количество повторений от 1000 до 100 для каждого теста. Я считаю, что это по-прежнему статистически жизнеспособно.
Ssize = 10000;
NumIterations = 100;
NumInsertions = round(logspace(0, 4, 10));
NumInitialNZ = round(logspace(1, 4, 4));
NumTests = numel(NumInsertions) * numel(NumInitialNZ);
TimeDirect = zeros(numel(NumInsertions), numel(NumInitialNZ));
TimeIndices = zeros(numel(NumInsertions), 1);
%% Single insertion operation (non-incremental)
% Method A: Direct insertion
for iInitialNZ = 1:numel(NumInitialNZ)
disp(['Running with initial nzmax = ' num2str(NumInitialNZ(iInitialNZ))]);
for iInsertions = 1:numel(NumInsertions)
tSum = 0;
for jj = 1:NumIterations
S = spalloc(Ssize, Ssize, NumInitialNZ(iInitialNZ));
r = randi(Ssize, NumInsertions(iInsertions), 1);
c = randi(Ssize, NumInsertions(iInsertions), 1);
tic
S(r,c) = 1;
tSum = tSum + toc;
end
disp([num2str(NumInsertions(iInsertions)) ' direct insertions: ' num2str(tSum) ' seconds']);
TimeDirect(iInsertions, iInitialNZ) = tSum;
end
end
% Method B: Rebuilding from index vectors
for iInsertions = 1:numel(NumInsertions)
tSum = 0;
for jj = 1:NumIterations
i = []; j = []; s = [];
r = randi(Ssize, NumInsertions(iInsertions), 1);
c = randi(Ssize, NumInsertions(iInsertions), 1);
s_ones = ones(NumInsertions(iInsertions), 1);
tic
i_new = [i; r];
j_new = [j; c];
s_new = [s; s_ones];
S = sparse(i_new, j_new ,s_new , Ssize, Ssize);
tSum = tSum + toc;
end
disp([num2str(NumInsertions(iInsertions)) ' indexed insertions: ' num2str(tSum) ' seconds']);
TimeIndices(iInsertions) = tSum;
end
SingleOperation.TimeDirect = TimeDirect;
SingleOperation.TimeIndices = TimeIndices;
%% Incremental insertion
for iInitialNZ = 1:numel(NumInitialNZ)
disp(['Running with initial nzmax = ' num2str(NumInitialNZ(iInitialNZ))]);
% Method A: Direct insertion
for iInsertions = 1:numel(NumInsertions)
tSum = 0;
for jj = 1:NumIterations
S = spalloc(Ssize, Ssize, NumInitialNZ(iInitialNZ));
r = randi(Ssize, NumInsertions(iInsertions), 1);
c = randi(Ssize, NumInsertions(iInsertions), 1);
tic
for ii = 1:NumInsertions(iInsertions)
S(r(ii),c(ii)) = 1;
end
tSum = tSum + toc;
end
disp([num2str(NumInsertions(iInsertions)) ' direct insertions: ' num2str(tSum) ' seconds']);
TimeDirect(iInsertions, iInitialNZ) = tSum;
end
end
% Method B: Rebuilding from index vectors
for iInsertions = 1:numel(NumInsertions)
tSum = 0;
for jj = 1:NumIterations
i = []; j = []; s = [];
r = randi(Ssize, NumInsertions(iInsertions), 1);
c = randi(Ssize, NumInsertions(iInsertions), 1);
tic
for ii = 1:NumInsertions(iInsertions)
i = [i; r(ii)];
j = [j; c(ii)];
s = [s; 1];
S = sparse(i, j ,s , Ssize, Ssize);
end
tSum = tSum + toc;
end
disp([num2str(NumInsertions(iInsertions)) ' indexed insertions: ' num2str(tSum) ' seconds']);
TimeIndices(iInsertions) = tSum;
end
IncremenalInsertion.TimeDirect = TimeDirect;
IncremenalInsertion.TimeIndices = TimeIndices;
%% Plot results
% Single insertion
figure;
loglog(NumInsertions, SingleOperation.TimeIndices);
cellLegend = {'Using index vectors'};
hold all;
for iInitialNZ = 1:numel(NumInitialNZ)
loglog(NumInsertions, SingleOperation.TimeDirect(:, iInitialNZ));
cellLegend = [cellLegend; {['Direct insertion, initial nzmax = ' num2str(NumInitialNZ(iInitialNZ))]}];
end
hold off;
title('Benchmark for single insertion operation');
xlabel('Number of insertions'); ylabel('Runtime for 100 operations [sec]');
legend(cellLegend, 'Location', 'NorthWest');
grid on;
% Incremental insertions
figure;
loglog(NumInsertions, IncremenalInsertion.TimeIndices);
cellLegend = {'Using index vectors'};
hold all;
for iInitialNZ = 1:numel(NumInitialNZ)
loglog(NumInsertions, IncremenalInsertion.TimeDirect(:, iInitialNZ));
cellLegend = [cellLegend; {['Direct insertion, initial nzmax = ' num2str(NumInitialNZ(iInitialNZ))]}];
end
hold off;
title('Benchmark for incremental insertions');
xlabel('Number of insertions'); ylabel('Runtime for 100 operations [sec]');
legend(cellLegend, 'Location', 'NorthWest');
grid on;
Я запустил это в MATLAB R2012a. Результаты для отдельных операций вставки суммируются на этом графике:
Это показывает, что использование прямой вставки намного медленнее, чем использование индексных векторов, если выполняется только одна операция. Рост в случае использования индексных векторов может быть либо из-за роста самих векторов, либо из более длинной разреженной матрицы, я не уверен, что. Начальный nzmax
, используемый для построения матриц, по-видимому, не влияет на их рост.
Результаты для инкрементных вставок суммируются на этом графике:
Здесь мы видим противоположную тенденцию: использование индексных векторов происходит медленнее из-за накладных расходов их постепенного роста и восстановления разреженной матрицы на каждом шаге. Способ понять это - посмотреть на первую точку на предыдущем графике: для вставки одного элемента более эффективно использовать прямую вставку, а не перестраивать с использованием векторов индекса. В инкрементальном случае эта единственная вставка выполняется повторно, и поэтому становится жизнеспособным использовать прямую вставку, а не индексные векторы, против предложения MATLAB.
Это понимание также предполагает, что если бы мы постепенно добавляли, скажем, 100 элементов за раз, то эффективным выбором было бы использовать индексные векторы, а не прямую вставку, так как первый график показывает, что этот метод будет быстрее для вставки этот размер. Между этими двумя режимами есть область, где вы, вероятно, должны поэкспериментировать, чтобы определить, какой метод более эффективен, хотя, вероятно, результаты покажут, что разница между методами там невелика.
Нижняя строка: какой метод я должен использовать?
Мой вывод состоит в том, что это зависит от характера ваших предполагаемых операций вставки.
- Если вы собираетесь вставлять элементы по одному, используйте прямую вставку.
- Если вы намереваетесь вставлять большое ( > 10) количество элементов за раз, перестройте матрицу из индексных векторов.