Постановка задачи
Это обычная борьба, и нет ничего лучше теста, чтобы ответить. Вот мои предположения:
Хорошо отформатированный файл ASCII, содержащий два столбца чисел. Без заголовков, непоследовательных строк и т. Д.
Метод должен масштабироваться для чтения файлов, которые слишком велики для того, чтобы помещаться в память (хотя мое терпение ограничено, поэтому мой тестовый файл занимает всего 500 000 строк).
Фактическая операция (которую ОП называет «делать вещи с числами») должна выполняться по одной строке за раз, не может быть векторизована.
Обсуждение
Имея это в виду, ответы и комментарии, кажется, обнадеживают эффективность в трех областях:
- чтение файла большими партиями
- более эффективное выполнение преобразования строки в число (с помощью пакетной обработки или с использованием более качественных функций)
- делает фактическую обработку более эффективной (что я исключил из правила 3 выше).
Результаты
Я собрал быстрый скрипт для проверки скорости приема (и согласованности результата) 6 вариаций на эти темы. Результаты:
- Исходный код. 68,23 с . 582582 чек
- Использование sscanf, один раз в строке. 27,20 сек. 582582 чек
- Использование fscanf в больших партиях. 8,93 с . 582582 чек
- Использование текстового сканирования большими партиями. 8,79 с . 582582 чек
- Чтение больших партий в память, затем sscanf. 8,15 сек. 582582 чек
- Использование однострочного считывателя файлов Java и sscanf на однострочных. 63,56 сек. 582582 чек
- Использование одноканального сканера токенов Java. 81,19 сек. 582582 чек
- Полностью пакетные операции (не соответствует). 1,02 сек. 508680 чек (нарушает правило 3)
Резюме
Более половины исходного времени (68 -> 27 секунд) было использовано из-за неэффективности вызова str2num, который можно удалить, переключив sscanf.
Около 2/3 оставшегося времени (27 -> 8 секунд) можно уменьшить, используя более крупные пакеты как для чтения файлов, так и для преобразования строк в числа.
Если мы хотим нарушить правило номер три в исходном посте, еще 7/8 времени можно сократить, переключившись на полностью числовую обработку. Однако некоторые алгоритмы не поддаются этому, поэтому мы оставляем это в покое. (Не «проверочное» значение не совпадает с последней записью.)
Наконец, в прямом противоречии с моим предыдущим редактированием в этом ответе, экономия недоступна при переключении доступных кэшированных Java, однострочных считывателей. На самом деле это решение в 2–3 раза медленнее, чем сопоставимый результат с одной строкой с использованием собственных считывателей. (63 против 27 секунд).
Пример кода для всех описанных выше решений приведен ниже.
Пример кода
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% Create a test file
cd(tempdir);
fName = 'demo_file.txt';
fid = fopen(fName,'w');
for ixLoop = 1:5
d = randi(1e6, 1e5,2);
fprintf(fid, '%d, %d \n',d);
end
fclose(fid);
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% Initial code
CHECK = 0;
tic;
fid = fopen('demo_file.txt');
tline = fgetl(fid);
while ischar(tline)
nums = str2num(tline);
CHECK = round((CHECK + mean(nums) ) /2);
tline = fgetl(fid);
end
fclose(fid);
t = toc;
fprintf(1,'Initial code. %3.2f sec. %d check \n', t, CHECK);
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% Using sscanf, once per line
CHECK = 0;
tic;
fid = fopen('demo_file.txt');
tline = fgetl(fid);
while ischar(tline)
nums = sscanf(tline,'%d, %d');
CHECK = round((CHECK + mean(nums) ) /2);
tline = fgetl(fid);
end
fclose(fid);
t = toc;
fprintf(1,'Using sscanf, once per line. %3.2f sec. %d check \n', t, CHECK);
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% Using fscanf in large batches
CHECK = 0;
tic;
bufferSize = 1e4;
fid = fopen('demo_file.txt');
scannedData = reshape(fscanf(fid, '%d, %d', bufferSize),2,[])' ;
while ~isempty(scannedData)
for ix = 1:size(scannedData,1)
nums = scannedData(ix,:);
CHECK = round((CHECK + mean(nums) ) /2);
end
scannedData = reshape(fscanf(fid, '%d, %d', bufferSize),2,[])' ;
end
fclose(fid);
t = toc;
fprintf(1,'Using fscanf in large batches. %3.2f sec. %d check \n', t, CHECK);
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% Using textscan in large batches
CHECK = 0;
tic;
bufferSize = 1e4;
fid = fopen('demo_file.txt');
scannedData = textscan(fid, '%d, %d \n', bufferSize) ;
while ~isempty(scannedData{1})
for ix = 1:size(scannedData{1},1)
nums = [scannedData{1}(ix) scannedData{2}(ix)];
CHECK = round((CHECK + mean(nums) ) /2);
end
scannedData = textscan(fid, '%d, %d \n', bufferSize) ;
end
fclose(fid);
t = toc;
fprintf(1,'Using textscan in large batches. %3.2f sec. %d check \n', t, CHECK);
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% Reading in large batches into memory, incrementing to end-of-line, sscanf
CHECK = 0;
tic;
fid = fopen('demo_file.txt');
bufferSize = 1e4;
eol = sprintf('\n');
dataBatch = fread(fid,bufferSize,'uint8=>char')';
dataIncrement = fread(fid,1,'uint8=>char');
while ~isempty(dataIncrement) && (dataIncrement(end) ~= eol) && ~feof(fid)
dataIncrement(end+1) = fread(fid,1,'uint8=>char'); %This can be slightly optimized
end
data = [dataBatch dataIncrement];
while ~isempty(data)
scannedData = reshape(sscanf(data,'%d, %d'),2,[])';
for ix = 1:size(scannedData,1)
nums = scannedData(ix,:);
CHECK = round((CHECK + mean(nums) ) /2);
end
dataBatch = fread(fid,bufferSize,'uint8=>char')';
dataIncrement = fread(fid,1,'uint8=>char');
while ~isempty(dataIncrement) && (dataIncrement(end) ~= eol) && ~feof(fid)
dataIncrement(end+1) = fread(fid,1,'uint8=>char');%This can be slightly optimized
end
data = [dataBatch dataIncrement];
end
fclose(fid);
t = toc;
fprintf(1,'Reading large batches into memory, then sscanf. %3.2f sec. %d check \n', t, CHECK);
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% Using Java single line readers + sscanf
CHECK = 0;
tic;
bufferSize = 1e4;
reader = java.io.LineNumberReader(java.io.FileReader('demo_file.txt'),bufferSize );
tline = char(reader.readLine());
while ~isempty(tline)
nums = sscanf(tline,'%d, %d');
CHECK = round((CHECK + mean(nums) ) /2);
tline = char(reader.readLine());
end
reader.close();
t = toc;
fprintf(1,'Using java single line file reader and sscanf on single lines. %3.2f sec. %d check \n', t, CHECK);
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% Using Java scanner for file reading and string conversion
CHECK = 0;
tic;
jFile = java.io.File('demo_file.txt');
scanner = java.util.Scanner(jFile);
scanner.useDelimiter('[\s\,\n\r]+');
while scanner.hasNextInt()
nums = [scanner.nextInt() scanner.nextInt()];
CHECK = round((CHECK + mean(nums) ) /2);
end
scanner.close();
t = toc;
fprintf(1,'Using java single item token scanner. %3.2f sec. %d check \n', t, CHECK);
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% Reading in large batches into memory, vectorized operations (non-compliant solution)
CHECK = 0;
tic;
fid = fopen('demo_file.txt');
bufferSize = 1e4;
eol = sprintf('\n');
dataBatch = fread(fid,bufferSize,'uint8=>char')';
dataIncrement = fread(fid,1,'uint8=>char');
while ~isempty(dataIncrement) && (dataIncrement(end) ~= eol) && ~feof(fid)
dataIncrement(end+1) = fread(fid,1,'uint8=>char'); %This can be slightly optimized
end
data = [dataBatch dataIncrement];
while ~isempty(data)
scannedData = reshape(sscanf(data,'%d, %d'),2,[])';
CHECK = round((CHECK + mean(scannedData(:)) ) /2);
dataBatch = fread(fid,bufferSize,'uint8=>char')';
dataIncrement = fread(fid,1,'uint8=>char');
while ~isempty(dataIncrement) && (dataIncrement(end) ~= eol) && ~feof(fid)
dataIncrement(end+1) = fread(fid,1,'uint8=>char');%This can be slightly optimized
end
data = [dataBatch dataIncrement];
end
fclose(fid);
t = toc;
fprintf(1,'Fully batched operations. %3.2f sec. %d check \n', t, CHECK);
(оригинальный ответ)
Если говорить подробнее о том, что сказал Бен, то узким местом всегда будет файловый ввод-вывод, если вы читаете эти файлы построчно.
Я понимаю, что иногда вы не можете поместить целый файл в память. Обычно я читаю большой набор символов (1e5, 1e6 или около того, в зависимости от памяти вашей системы). Затем я либо читаю дополнительные одиночные символы (или отступаю от одиночных символов), чтобы получить округленное число строк, а затем запускаю анализ вашей строки (например, sscanf).
Затем, если хотите, вы можете обработать полученную большую матрицу по одной строке за раз, прежде чем повторять процесс, пока не прочитаете конец файла.
Это немного утомительно, но не так сложно. Обычно я вижу улучшение скорости на 90% по сравнению с однострочными считывателями.
(ужасная идея с использованием считывателей с прерывистой линией Java, удаленных от стыда)