MATLAB 性能基准测试
Posted
技术标签:
【中文标题】MATLAB 性能基准测试【英文标题】:MATLAB performance benchmarking 【发布时间】:2014-12-19 04:11:43 【问题描述】:设置:
这篇文章是关于测试以下问题的解决方案的性能:
给出了一个由
S
字符串组成的元胞数组,格式为由下划线分隔的数字,例如:
list_of_words = repmat('02_04_04_52_23_14_54_672_0',10,1);
保证所有字符串:
它们只包含十进制数字和下划线; 下划线的个数相同; 没有两个连续的下划线。
编写 MATLAB 代码,将字符串元胞数组转换为双精度数矩阵
S
×M
(值小 1000 倍),其中S
是字符串个数,M
是字符串中的数字个数。
在 *** 上发布了一个非常相似的问题,并提出了几种解决方案。最初的目标是提高速度性能。
代码:
这两种为提高速度而设计的解决方案似乎在从一个 MATLAB 安装到另一个,甚至从一个运行到另一个运行时,性能差异很大。此外,它们的实现风格也完全不同。
在黑暗的角落,您会发现一个违反 MATLAB 最神圣原则的解决方案:eval
是邪恶的,应该不惜一切代价避免循环。但是,代码尝试通过避免重复的内存分配、使用将字符串转换为数字的快速方法以及进行就地操作来优化速度:
%'eval_and_loops_solution.m'
function array_of_numbers = eval_and_loops_solution(list_of_words)
n_numbers = 1 + sum(list_of_words1=='_');
n_words = numel(list_of_words);
array_of_numbers = zeros(n_numbers, n_words);
for k = 1:n_words
temp = ['[', list_of_wordsk, ']'];
temp(temp=='_') = ';';
array_of_numbers(:,k) = eval(temp);
end;
%'this is a waste of memory, but is kind of required'
array_of_numbers = transpose(array_of_numbers / 1000);
end
注意:原解返回结果为M
×S
double
数组。代码改编为S
×M
;但是,这种适应会消耗两倍的内存。是的,我写了这个解决方案。
在清晰的角落,您会找到一个真正符合 MATLAB 精神的解决方案,它避免使用循环和 eval
,而倾向于使用单个 sscanf
读取 all字符串(这是一种避免调用函数 S
次开销的巧妙方法):
%'single_sscanf_solution.m'
function array_of_numbers = single_sscanf_solution(list_of_words)
N = 1 + sum(list_of_words1=='_'); %'hard-coded in the original'
lens = cellfun(@numel,list_of_words);
tlens = sum(lens);
idx(1,tlens)=0;
idx(cumsum(lens(1:end-1))+1)=1;
idx2 = (1:tlens) + cumsum(idx);
one_str(1:max(idx2)+1)='_';
one_str(idx2) = [list_of_words:];
delim = repmat('%d_',1,N*numel(lens));
array_of_numbers = reshape(sscanf(one_str, delim),N,[])'./1000;
end
注意:此解决方案属于@Divakar。
裁判由两个功能组成:一个是生成测试用例,一个是做计时。
测试用例生成器组在一个单元数组中,由 1 到 9999 之间的 10 个随机整数中的下划线分隔的字符串组成(为了简单起见);但是,实现应该只假设数字是正数或零,并且数字应该适合double
:
%'referee_test_case.m'
function list_of_words = referee_test_case(n_words)
NUM_PER_WORD = 10;
NUM_MAX = 9999;
word_format = [repmat('%d_', 1, NUM_PER_WORD-1), '%d'];
list_of_words = cell(n_words,1);
fprintf('Generating %d-words test case...\n', n_words);
for k = 1:n_words
list_of_wordsk = sprintf(word_format, randi(NUM_MAX, [1, NUM_PER_WORD]));
end;
end
计时函数将测试用例和任意数量的处理函数句柄作为参数;它的实现例如一个功能中的错误不应该打扰其他功能的执行。它使用@Divakar 和@LuisMendo 推荐的timeit
;对于那些在默认 MATLAB 安装中没有此功能的人,可以从 MATLAB Central / File Exchange 下载它:
%'referee_timing.m'
function referee_timing(test_case, varargin)
%' Specify the test case as a cell array of strings, then the function '
%' handles that will process the test case. '
%' '
%' The function uses timeit() to evaluate the performance of different '
%' processing handles; if your MATLAB installation does not have it '
%' natively, download the available version from File Exchange: '
%' '
%' http://www.mathworks.com/matlabcentral/fileexchange/18798-timeit-benchmarking-function '
fprintf('Timing %d-words test case...\n', numel(test_case));
for k = 1:numel(varargin)
try
t = timeit(@() varargink(test_case));
fprintf(' %s: %f[s]\n', func2str(varargink), t);
catch ME
fprintf(' %s: Error - %s\n', func2str(varargink), ME.message);
end;
end;
end
问题:
如前所述,结果似乎从一个 MATLAB 安装到另一个,甚至从一个运行到另一个。我提出的问题有三个方面:
-
在您的特定 MATLAB 安装(版本 + 操作系统 + 硬件)上,这两种解决方案的性能相比如何?
是否有可能以相当大的方式改进一种或另一种解决方案?
是否有更快的基于完全不同的想法/功能的 MATLAB 原生(例如,没有 MEX 或特殊工具箱)解决方案?
对于第 1 点(基准测试),请在您的 MATLAB 路径中创建四个函数文件 eval_and_loops_solution.m
、single_sscanf_solution.m
、referee_test_case.m
、referee_timig.m
以及您可能想要测试的其他函数(例如,答案提出的实现)。将它们用于多个单词,例如喜欢这个脚本:
%'test.m'
clc;
feature accel on; %'tune for speed'
%'low memory stress'
referee_timing( ...
referee_test_case(10^4), ...
@eval_and_loops_solution, ...
@single_sscanf_solution ... %'add here other functions'
);
%'medium memory stress'
referee_timing( ...
referee_test_case(10^5), ...
@eval_and_loops_solution, ...
@single_sscanf_solution ... %'add here other functions'
);
%'high memory stress'
referee_timing( ...
referee_test_case(10^6), ...
@eval_and_loops_solution, ...
@single_sscanf_solution ... %'add here other functions'
);
发布结果时,请说明您的 MATLAB 版本、操作系统、RAM 大小和 CPU 型号。 请注意,对于大量单词,这些测试可能需要一些时间!但是,运行此脚本不会改变您当前工作区的内容。
对于第 2 点或第 3 点,您可以发布使用与 eval_and_loops_solution.m
和 single_sscanf_solution.m
相同的输入/输出约定的代码,以及支持该声明的基准测试。
赏金:
我会对每个基准测试答案进行投票,我鼓励每个人都这样做。对于那些用他们的技能、时间和处理能力做出贡献的人来说,这是最少的事情。
+500 赏金奖金将授予最快代码的作者,无论是提议的两个代码之一,还是优于他们的第三个新代码。我真的希望这将符合普遍协议。奖金将在原发帖日期后的月周内发放。
【问题讨论】:
哈哈你不会放弃吧!? :) @Divakar 哈哈,不。 :-) 我还是很好奇。也希望现在发帖的语气更贴切。 语气很贴切,也很搞笑! 所有单元格的字符数是否相同(即左填充为零)? 另外,你确定 tic/toc 内部函数是个好主意吗?也许改用timeit
?
【参考方案1】:
这建立在 Amro 的 MEX 解决方案之上。由于 CST-Link 定义的问题受到如此严格的限制,因此可以通过牺牲稳健性和放弃错误处理来实现快速实现。因此,按照指定格式输入!
将 Amro 源代码中的主循环替换为以下代码,可提高约 4 倍的速度,无需多线程 (full source):
// extract numbers from strings
for (mwIndex idx=0, i=0; idx<n_words; ++idx)
std::string str = getString(cellstr, idx);
std::string::const_iterator istr = str.cbegin();
for (; istr != str.cend(); ++istr)
if (*istr < '0' || *istr > '9')
++i;
continue;
out[i] *= 10;
out[i] += *istr - '0';
++i;
可以做更多的手动优化,但我要睡觉了。另外,我计划在某个时候使用多线程版本,但添加 pragma 非常简单。
基准(更新的 T-距宽限期结束还有 12 小时)
使用 Amro 的最新功能包和 Ben Voigt 的解决方案进行的基准测试显示不同机器上的不同时序,但值得一提的是,一些解决方案之间似乎存在虚拟联系。在我的带有 R2014a 64 位 Windows 8(在 CUDA 上)的 i7 笔记本电脑上,我的 textscan、Divakar 的最新解决方案和 Ben Voigt 的解决方案是最好的,性能差异无法区分。 CST-Link 的“所有循环”非常接近,但始终比其他循环慢一点。这些基准测试最多使用 1e6 个字符串。 元胞数组的大小决定最快的方法。
取出 MEX 解决方案(以及 GPU,因为我的笔记本电脑),解决方案针对不同的行数放置如下。同样,结果表明适当的方法取决于行数:
i7 16GB R2014a
解决方案 1e6 5e5 1e5 1e4 1e3 100 ____________________________________ ____ ____ ____ ____ ____ ____ 'solution_textscan_sprintf_chappjc' 1 2 2 5 5 4 'solution_bsxfun_sprintf_Divakar' 2 1 3 2 1 8 'func_voigt_loop' 3 3 5 1 2 1 'func_textscan_sprintf' 4 4 4 4 6 3 'solution_bsxfun_bytestream_Divakar' 5 5 6 3 3 10 'solution_loops_CSTLink' 6 7 7 7 8 6 'func_sscanf_sprintf' 7 6 1 6 7 7 'solution_bsxfun_cumsum_Divakar' 8 8 8 8 4 9 'solution_sscanf_char_LuisMendo' 9 9 9 9 9 11 'func_textscan_strjoin' 10 10 15 10 10 16 'func_sscanf_strjoin' 11 11 10 11 11 19 'func_eval_cellfun' 12 12 13 12 12 18 'func_eval_loop' 13 13 11 13 13 12 'solution_eval_loops_CSTLink' 14 15 14 14 14 13 'func_sscanf_loop' 15 14 12 15 15 15 'func_textscan_loop' 16 18 19 18 18 21 'func_sscanf_cellfun' 17 17 16 17 17 22 'func_str2num_cellfun' 18 19 18 19 19 23 'func_str2num_loop' 19 20 20 20 20 24 'solution_sscanf_Divakar' 20 16 17 16 16 20 'func_textscan_cellfun' 21 21 21 21 21 25 'func_str2num_sprintf' 22 23 23 22 22 5 'func_str2num_strjoin' 23 24 25 23 23 17 'func_eval_sprintf' 24 22 24 24 24 2 'func_eval_strjoin' 25 25 22 25 25 14>> t
t =
func nrows ncols time
____________________________________ _____ _____ __________
'func_eval_cellfun' 100 10 0.00074097
'func_eval_loop' 100 10 0.0006137
'func_eval_sprintf' 100 10 0.00017814
'func_eval_strjoin' 100 10 0.00068062
'func_sscanf_cellfun' 100 10 0.0012905
'func_sscanf_loop' 100 10 0.00069992
'func_sscanf_sprintf' 100 10 0.00022678
'func_sscanf_strjoin' 100 10 0.00075428
'func_str2num_cellfun' 100 10 0.0014366
'func_str2num_loop' 100 10 0.0014904
'func_str2num_sprintf' 100 10 0.00020667
'func_str2num_strjoin' 100 10 0.00073786
'func_textscan_cellfun' 100 10 0.0018517
'func_textscan_loop' 100 10 0.0012629
'func_textscan_sprintf' 100 10 0.00020092
'func_textscan_strjoin' 100 10 0.00071305
'func_voigt_loop' 100 10 0.00017711
'solution_bsxfun_bytestream_Divakar' 100 10 0.00029257
'solution_bsxfun_cumsum_Divakar' 100 10 0.00028395
'solution_bsxfun_sprintf_Divakar' 100 10 0.00023879
'solution_eval_loops_CSTLink' 100 10 0.00066461
'solution_loops_CSTLink' 100 10 0.00021923
'solution_mex_Amro' 100 10 0.00020502
'solution_mex_chappjc' 100 10 6.3164e-05
'solution_mex_omp_Amro' 100 10 0.00018224
'solution_mex_omp_chappjc' 100 10 8.2565e-05
'solution_sscanf_Divakar' 100 10 0.00084008
'solution_sscanf_char_LuisMendo' 100 10 0.00033844
'solution_textscan_sprintf_chappjc' 100 10 0.00020396
'func_eval_cellfun' 1000 10 0.0058036
'func_eval_loop' 1000 10 0.0060269
'func_eval_sprintf' 1000 10 0.055797
'func_eval_strjoin' 1000 10 0.057631
'func_sscanf_cellfun' 1000 10 0.011712
'func_sscanf_loop' 1000 10 0.0067405
'func_sscanf_sprintf' 1000 10 0.0019112
'func_sscanf_strjoin' 1000 10 0.0040608
'func_str2num_cellfun' 1000 10 0.013712
'func_str2num_loop' 1000 10 0.014961
'func_str2num_sprintf' 1000 10 0.04916
'func_str2num_strjoin' 1000 10 0.051823
'func_textscan_cellfun' 1000 10 0.017256
'func_textscan_loop' 1000 10 0.012454
'func_textscan_sprintf' 1000 10 0.0016489
'func_textscan_strjoin' 1000 10 0.0038387
'func_voigt_loop' 1000 10 0.0012892
'solution_bsxfun_bytestream_Divakar' 1000 10 0.0013951
'solution_bsxfun_cumsum_Divakar' 1000 10 0.0015138
'solution_bsxfun_sprintf_Divakar' 1000 10 0.0011496
'solution_eval_loops_CSTLink' 1000 10 0.0061538
'solution_loops_CSTLink' 1000 10 0.0020528
'solution_mex_Amro' 1000 10 0.0019629
'solution_mex_chappjc' 1000 10 0.00051825
'solution_mex_omp_Amro' 1000 10 0.00085117
'solution_mex_omp_chappjc' 1000 10 0.00025825
'solution_sscanf_Divakar' 1000 10 0.0078551
'solution_sscanf_char_LuisMendo' 1000 10 0.0031104
'solution_textscan_sprintf_chappjc' 1000 10 0.0016144
'func_eval_cellfun' 10000 10 0.05772
'func_eval_loop' 10000 10 0.061705
'func_eval_sprintf' 10000 10 0.54464
'func_eval_strjoin' 10000 10 0.57007
'func_sscanf_cellfun' 10000 10 0.1192
'func_sscanf_loop' 10000 10 0.068017
'func_sscanf_sprintf' 10000 10 0.019622
'func_sscanf_strjoin' 10000 10 0.038232
'func_str2num_cellfun' 10000 10 0.13811
'func_str2num_loop' 10000 10 0.14812
'func_str2num_sprintf' 10000 10 0.48726
'func_str2num_strjoin' 10000 10 0.50528
'func_textscan_cellfun' 10000 10 0.17378
'func_textscan_loop' 10000 10 0.1241
'func_textscan_sprintf' 10000 10 0.016595
'func_textscan_strjoin' 10000 10 0.035599
'func_voigt_loop' 10000 10 0.012136
'solution_bsxfun_bytestream_Divakar' 10000 10 0.015908
'solution_bsxfun_cumsum_Divakar' 10000 10 0.02301
'solution_bsxfun_sprintf_Divakar' 10000 10 0.014862
'solution_eval_loops_CSTLink' 10000 10 0.063188
'solution_loops_CSTLink' 10000 10 0.020153
'solution_mex_Amro' 10000 10 0.019252
'solution_mex_chappjc' 10000 10 0.0051221
'solution_mex_omp_Amro' 10000 10 0.0066551
'solution_mex_omp_chappjc' 10000 10 0.0014584
'solution_sscanf_Divakar' 10000 10 0.096345
'solution_sscanf_char_LuisMendo' 10000 10 0.031047
'solution_textscan_sprintf_chappjc' 10000 10 0.016736
'func_eval_cellfun' 1e+05 10 0.78876
'func_eval_loop' 1e+05 10 0.6119
'func_eval_sprintf' 1e+05 10 6.7603
'func_eval_strjoin' 1e+05 10 5.7204
'func_sscanf_cellfun' 1e+05 10 1.2096
'func_sscanf_loop' 1e+05 10 0.68303
'func_sscanf_sprintf' 1e+05 10 0.21101
'func_sscanf_strjoin' 1e+05 10 0.55226
'func_str2num_cellfun' 1e+05 10 1.411
'func_str2num_loop' 1e+05 10 1.8229
'func_str2num_sprintf' 1e+05 10 6.1474
'func_str2num_strjoin' 1e+05 10 7.551
'func_textscan_cellfun' 1e+05 10 2.5898
'func_textscan_loop' 1e+05 10 1.7934
'func_textscan_sprintf' 1e+05 10 0.25421
'func_textscan_strjoin' 1e+05 10 1.1762
'func_voigt_loop' 1e+05 10 0.25602
'solution_bsxfun_bytestream_Divakar' 1e+05 10 0.265
'solution_bsxfun_cumsum_Divakar' 1e+05 10 0.35656
'solution_bsxfun_sprintf_Divakar' 1e+05 10 0.23481
'solution_eval_loops_CSTLink' 1e+05 10 0.86425
'solution_loops_CSTLink' 1e+05 10 0.28436
'solution_mex_Amro' 1e+05 10 0.27104
'solution_mex_chappjc' 1e+05 10 0.078901
'solution_mex_omp_Amro' 1e+05 10 0.096553
'solution_mex_omp_chappjc' 1e+05 10 0.03679
'solution_sscanf_Divakar' 1e+05 10 1.3818
'solution_sscanf_char_LuisMendo' 1e+05 10 0.43994
'solution_textscan_sprintf_chappjc' 1e+05 10 0.21271
'func_eval_cellfun' 5e+05 10 3.7658
'func_eval_loop' 5e+05 10 3.8106
'func_eval_sprintf' 5e+05 10 32.383
'func_eval_strjoin' 5e+05 10 40.451
'func_sscanf_cellfun' 5e+05 10 8.5704
'func_sscanf_loop' 5e+05 10 4.707
'func_sscanf_sprintf' 5e+05 10 1.4362
'func_sscanf_strjoin' 5e+05 10 2.8301
'func_str2num_cellfun' 5e+05 10 9.6439
'func_str2num_loop' 5e+05 10 10.453
'func_str2num_sprintf' 5e+05 10 35.818
'func_str2num_strjoin' 5e+05 10 37.277
'func_textscan_cellfun' 5e+05 10 12.418
'func_textscan_loop' 5e+05 10 8.8237
'func_textscan_sprintf' 5e+05 10 1.2793
'func_textscan_strjoin' 5e+05 10 2.6496
'func_voigt_loop' 5e+05 10 1.2486
'solution_bsxfun_bytestream_Divakar' 5e+05 10 1.324
'solution_bsxfun_cumsum_Divakar' 5e+05 10 1.8229
'solution_bsxfun_sprintf_Divakar' 5e+05 10 1.2113
'solution_eval_loops_CSTLink' 5e+05 10 6.5759
'solution_loops_CSTLink' 5e+05 10 1.4583
'solution_mex_Amro' 5e+05 10 1.3718
'solution_mex_chappjc' 5e+05 10 0.39711
'solution_mex_omp_Amro' 5e+05 10 0.48046
'solution_mex_omp_chappjc' 5e+05 10 0.48174
'solution_sscanf_Divakar' 5e+05 10 7.7943
'solution_sscanf_char_LuisMendo' 5e+05 10 2.2332
'solution_textscan_sprintf_chappjc' 5e+05 10 1.2399
'func_eval_cellfun' 1e+06 10 7.3884
'func_eval_loop' 1e+06 10 7.5519
'func_eval_sprintf' 1e+06 10 69.868
'func_eval_strjoin' 1e+06 10 71.964
'func_sscanf_cellfun' 1e+06 10 15.061
'func_sscanf_loop' 1e+06 10 8.4163
'func_sscanf_sprintf' 1e+06 10 2.7099
'func_sscanf_strjoin' 1e+06 10 5.1453
'func_str2num_cellfun' 1e+06 10 17.42
'func_str2num_loop' 1e+06 10 18.165
'func_str2num_sprintf' 1e+06 10 60.902
'func_str2num_strjoin' 1e+06 10 63.579
'func_textscan_cellfun' 1e+06 10 20.423
'func_textscan_loop' 1e+06 10 14.309
'func_textscan_sprintf' 1e+06 10 2.2853
'func_textscan_strjoin' 1e+06 10 4.5216
'func_voigt_loop' 1e+06 10 2.2443
'solution_bsxfun_bytestream_Divakar' 1e+06 10 2.3495
'solution_bsxfun_cumsum_Divakar' 1e+06 10 3.3843
'solution_bsxfun_sprintf_Divakar' 1e+06 10 2.0311
'solution_eval_loops_CSTLink' 1e+06 10 7.7524
'solution_loops_CSTLink' 1e+06 10 2.4947
'solution_mex_Amro' 1e+06 10 2.486
'solution_mex_chappjc' 1e+06 10 0.76551
'solution_mex_omp_Amro' 1e+06 10 0.92226
'solution_mex_omp_chappjc' 1e+06 10 0.88736
'solution_sscanf_Divakar' 1e+06 10 19.673
'solution_sscanf_char_LuisMendo' 1e+06 10 3.8578
'solution_textscan_sprintf_chappjc' 1e+06 10 2.0074
下一步...
X5550 24GB R2014b
顺序不同,但差异又微不足道。
时间超过 30000 个字符的限制,但如果需要,我可以将它们发布到某个地方。不过顺序很清楚。
我建议 CST-Link 在决定时忽略所有这些衡量标准。
当然,MEX 解决方案规则。上面使用std::string::const_iterator istr
的解决方案很快,使用 OpenMP 甚至更快。 GCC 4.9.1 (pthreads) 和 VS2013 对此代码的性能相当。 Threaded source here.
【讨论】:
您尝试过不同的 C++ 编译器吗?出于某种原因,VS2013 的速度至少是 GCC 的两倍(我正在使用来自here 的最新 MinGW-w64 GCC 4.9.1)。线程版本更糟糕.. @Amro istringstream 一个还是这个?但是,不,我没有与 GCC 进行比较,尽管我确实从我正在做的 ffmpeg 构建中安装了 MinGW-w64(除了 win32threads 版本之外与你的相同)。我以后可以试试。顺便说一句,我没有时间让这段代码漂亮,这里没有聪明之处。 不,我指的是带有stringstream
的版本。我还没有根据您的修改重新运行基准测试,但我确实看到解析字符串会更快。
我更新了我的测试结果。你的修改版本是最快的,恭喜!
漂亮的新图表。标题都说 2014b,但答案中的标题说 i7 是 2014a?【参考方案2】:
在 MATLAB 节日精神中,这里是一个高度矢量化的解决方案:
function M = func_voigt_loop(C)
S = sprintf('_%s', C:);
digitpos = [find(S(2:end) == '_'), numel(S)];
M = zeros(size(digitpos));
place = 1;
while 1
digits = S(digitpos);
mask = double(digits ~= '_');
M = M + mask.*(digits - '0') * place;
place = place * 10;
digitpos = digitpos - mask;
if ~any(mask); break; end
end
M = reshape(M, [], numel(C))';
end
它只有几行,易于理解(抓住每个数字的个位,添加十位等),并且不使用任何“异国情调”的功能,如eval
或textscan
。它也非常快。
这个想法类似于我之前开发的用于在 CSV 文件中的一整列数据上执行 datenum
的想法,即使指定了特定模式,MATLAB 版本也非常慢。 MATLAB 的后续版本显着改进了 datenum,但我的手动调整版本仍然轻松胜过它。
【讨论】:
很好的解决方案。 +1 显然,我喜欢sprintf
位和每个数字的 *10 方法,因为这就是我做 MEX 噱头答案的方式(取笑使用 MATLAB 获得最佳性能的想法)。但是,我认为textscan
远非异国情调,它现在是 MATLAB 中的主流文本处理功能。如果您浏览 release notes 以获取 MATLAB 的最后几个版本,很明显 MathWorks 一直专注于将 textscan
构建成一个非常快速的文本扫描器,在语法上类似于 sscanf
。
这也很快。这是我在我的机器上进行的快速测试得到的结果(大小 = 1e4 x 10
):i.stack.imgur.com/1HD7H.png。正如我之前所说,每次运行的结果确实会有所不同,因此请务必在您的机器上进行测试,然后再做出决定!
@chappjc:我有点用“异国情调”来表示不可移植。此答案中的所有内容都可以轻松地用其他语言复制。
@Amro:你有并行计算工具箱吗?我认为我们正在看到自动并行化,因为它从我的系统转移到您的系统获得了相对数量级。实际上,也许没有那么多。但它确实令人信服地超越了其他三四个最快的纯 MATLAB 解决方案。
@chappjc:感谢您花时间运行所有这些测试。我们当然有很多不错的实现可供选择!【参考方案3】:
这里有一堆实现供您进行基准测试(都是纯 MATLAB)。他们中的一些人借鉴了其他解决方案的想法。
function M = func_eval_strjoin(C)
M = eval(['[' strjoin(strrep(C,'_',' ').',';') ']']);
end
function M = func_eval_sprintf(C)
C = strrep(C,'_',' ');
M = eval(['[' sprintf('%s;',C:) ']']);
end
function M = func_eval_cellfun(C)
M = cell2mat(cellfun(@eval, strcat('[',strrep(C,'_',' '),']'), 'Uniform',false));
end
function M = func_eval_loop(C)
M = zeros(numel(C),nnz(C1=='_')+1);
for i=1:numel(C)
M(i,:) = eval(['[' strrep(Ci,'_',' ') ']']);
end
end
function M = func_str2num_strjoin(C)
M = reshape(str2num(strjoin(strrep(C.','_',' '))), [], numel(C)).';
end
function M = func_str2num_sprintf(C)
C = strrep(C,'_',' ');
M = reshape(str2num(sprintf('%s ',C:)), [], numel(C)).';
end
function M = func_str2num_cellfun(C)
M = cell2mat(cellfun(@str2num, strrep(C, '_', ' '), 'Uniform',false));
end
function M = func_str2num_loop(C)
M = zeros(numel(C),nnz(C1=='_')+1);
for i=1:numel(C)
M(i,:) = str2num(strrep(Ci, '_', ' '));
end
end
function M = func_sscanf_strjoin(C)
M = reshape(sscanf(strjoin(C', '_'), '%f_'), [], numel(C)).';
end
function M = func_sscanf_sprintf(C)
M = reshape(sscanf(sprintf('%s_',C:),'%f_'), [], numel(C)).';
end
function M = func_sscanf_cellfun(C)
M = cell2mat(cellfun(@(c) sscanf(c, '%f_'), C, 'Uniform',false).').';
end
function M = func_sscanf_loop(C)
M = zeros(numel(C),nnz(C1=='_')+1);
for i=1:numel(C)
M(i,:) = sscanf(Ci, '%f_');
end
end
function M = func_textscan_strjoin(C)
M = textscan(strjoin(C', '_'), '%.0f', 'Delimiter','_');
M = reshape(M1, [], numel(C)).';
end
function M = func_textscan_sprintf(C)
M = textscan(sprintf('%s_',C:),'%.0f', 'Delimiter','_');
M = reshape(M1, [], numel(C)).';
end
function M = func_textscan_cellfun(C)
M = cell2mat(cellfun(@(str) textscan(str, '%.0f', 'Delimiter','_'), C).').';
end
function M = func_textscan_loop(C)
M = zeros(numel(C),nnz(C1=='_')+1);
for i=1:numel(C)
x = textscan(Ci, '%.0f', 'Delimiter','_');
M(i,:) = x1;
end
end
function M = func_str2double_strsplit_strjoin(C)
M = reshape(str2double(strsplit(strjoin(C','_'),'_')), [], numel(C)).';
end
function M = func_str2double_strsplit_sprintf(C)
M = strsplit(sprintf('%s_',C:), '_');
M = reshape(str2double(M(1:end-1)), [], numel(C)).';
end
function M = func_str2double_strsplit(C)
M = cellfun(@(c) strsplit(c,'_'), C, 'Uniform',false);
M = str2double(cat(1,M:));
end
function M = func_str2double_strsplit_cellfun(C)
M = cell2mat(cellfun(@(c) str2double(strsplit(c,'_')), C, 'Uniform',false));
end
function M = func_str2double_strsplit_loop(C)
M = zeros(numel(C),nnz(C1=='_')+1);
for i=1:numel(C)
M(i,:) = str2double(strsplit(Ci,'_'));
end
end
function M = func_str2double_regex_split_strjoin(C)
M = reshape(str2double(regexp(strjoin(C.','_'), '_', 'split')), [], numel(C)).';
end
function M = func_str2double_regex_split_sprintf(C)
M = regexp(sprintf('%s_',C:), '_', 'split');
M = reshape(str2double(M(1:end-1)), [], numel(C)).';
end
function M = func_str2double_regex_split(C)
M = regexp(C, '_', 'split');
M = reshape(str2double([M:]), [], numel(C)).';
end
function M = func_str2double_regex_split_cellfun(C)
M = cell2mat(cellfun(@str2double, regexp(C, '_', 'split'), 'Uniform',false));
end
function M = func_str2double_regex_split_loop(C)
M = zeros(numel(C),nnz(C1=='_')+1);
for i=1:numel(C)
M(i,:) = str2double(regexp(Ci, '_', 'split'));
end
end
function M = func_str2double_regex_tokens_strjoin_1(C)
M = reshape(cellfun(@str2double, regexp(strjoin(C.','_'), '(\d+)', 'tokens')), [], numel(C)).';
end
function M = func_str2double_regex_tokens_strjoin_2(C)
M = regexp(strjoin(C.','_'), '(\d+)', 'tokens');
M = reshape(str2double([M:]), [], numel(C)).';
end
function M = func_str2double_regex_tokens_sprintf_1(C)
M = reshape(cellfun(@str2double, regexp(sprintf('%s_',C:), '(\d+)', 'tokens')), [], numel(C)).';
end
function M = func_str2double_regex_tokens_sprintf_2(C)
M = regexp(sprintf('%s_',C:), '(\d+)', 'tokens');
M = reshape(str2double([M:]), [], numel(C)).';
end
function M = func_str2double_regex_tokens(C)
M = regexp(C, '(\d+)', 'tokens');
M = cat(1,M:);
M = reshape(str2double([M:]), size(M));
end
function M = func_str2double_regex_tokens_cellfun(C)
M = regexp(C, '(\d+)', 'tokens');
M = cellfun(@str2double, cat(1,M:));
end
function M = func_str2double_regex_tokens_loop(C)
M = zeros(numel(C),nnz(C1=='_')+1);
for i=1:numel(C)
x = regexp(Ci, '(\d+)', 'tokens');
M(i,:) = str2double([x:]);
end
end
大多数方法都是相同想法的变体,只是实现方式略有不同(例如:使用显式 for 循环与cellfun
,使用strjoin
与sprintf
将字符串单元数组连接成一个字符串等..)。
让我再分解一下:
有基于eval
的解决方案。我们要么在循环中应用eval
,要么在将字符串拼合为一个之后进行一次调用。
同样有基于调用str2num
的解决方案(在用空格替换下划线之后)。值得注意的是str2num
本身在内部调用eval
。
还有sscanf
和textscan
解决方案。像以前一样,我们要么在循环中使用它们,要么调用一个长字符串。
另一组解决方案基于在用下划线分隔符分割字符串后调用str2double
。还有其他拆分字符串的方法(使用带有“split”选项的regex
,或使用strsplit
函数)。
终于有了一套基于正则表达式匹配的解决方案。
编辑:
我创建了一组函数来对各种实现进行基准测试 (GitHub Gist)。这是pre-packaged files,到目前为止已发布了所有解决方案。我包含了已编译的 MEX 函数(适用于 64 位 Windows)以及 MinGW-w64 DLL 依赖项。
这是在我的机器上运行的结果(四核 Intel Core i7 CPU、8GB、Win8.1、MATLAB R2014a 的笔记本电脑)。我测试了以下尺寸:10x10、100x10、1000x10 和 10000x10。如您所见,随着数据量的增加,MEX 解决方案的效果会更好...
编辑#2:
根据要求,我用最新的解决方案更新了我的测试结果;我添加了 @chappjc 的 MEX 文件的修改版本,以及 @Divakar 的 GPU 版本。这是文件的updated archive。
我使用与以前相同的机器。我已将单元阵列大小增加到 1e5。我的 GPU 设备非常适合笔记本电脑:
>> gpuDevice
ans =
CUDADevice with properties:
Name: 'GeForce GT 630M'
Index: 1
ComputeCapability: '2.1'
SupportsDouble: 1
DriverVersion: 5.5000
ToolkitVersion: 5.5000
MaxThreadsPerBlock: 1024
MaxShmemPerBlock: 49152
MaxThreadBlockSize: [1024 1024 64]
MaxGridSize: [65535 65535 65535]
SIMDWidth: 32
TotalMemory: 2.1475e+09
FreeMemory: 2.0453e+09
MultiprocessorCount: 2
ClockRateKHz: 950000
ComputeMode: 'Default'
GPUOverlapsTransfers: 1
KernelExecutionTimeout: 1
CanMapHostMemory: 1
DeviceSupported: 1
DeviceSelected: 1
以下是最新结果:
>> t
t =
func nrows ncols time
________________________________________ _____ _____ __________
'func_eval_cellfun' 10 10 0.00044645
'func_eval_loop' 10 10 0.0001554
'func_eval_sprintf' 10 10 7.6547e-05
'func_eval_strjoin' 10 10 0.00056739
'func_sscanf_cellfun' 10 10 0.00037247
'func_sscanf_loop' 10 10 0.00017182
'func_sscanf_sprintf' 10 10 8.4928e-05
'func_sscanf_strjoin' 10 10 0.00056388
'func_str2num_cellfun' 10 10 0.00039231
'func_str2num_loop' 10 10 0.00033852
'func_str2num_sprintf' 10 10 0.00010862
'func_str2num_strjoin' 10 10 0.00057953
'func_textscan_cellfun' 10 10 0.00044585
'func_textscan_loop' 10 10 0.00024666
'func_textscan_sprintf' 10 10 9.4507e-05
'func_textscan_strjoin' 10 10 0.00056123
'solution_bsxfun_bytestream_Divakar' 10 10 0.00018166
'solution_bsxfun_bytestream_gpu_Divakar' 10 10 0.0029487
'solution_bsxfun_cumsum_Divakar' 10 10 0.00016396
'solution_bsxfun_sprintf_Divakar' 10 10 0.00012932
'solution_bsxfun_sprintf_gpu_Divakar' 10 10 0.002315
'solution_eval_loops_CSTLink' 10 10 0.00017191
'solution_loops_CSTLink' 10 10 6.5514e-05
'solution_mex_Amro' 10 10 6.4487e-05
'solution_mex_chappjc' 10 10 4.2507e-05
'solution_mex_omp_Amro' 10 10 0.00027411
'solution_mex_omp_chappjc' 10 10 0.00013017
'solution_sscanf_Divakar' 10 10 0.00020458
'solution_sscanf_char_LuisMendo' 10 10 0.00011144
'solution_textscan_sprintf_chappjc' 10 10 0.00010528
'func_eval_cellfun' 100 10 0.0011801
'func_eval_loop' 100 10 0.001059
'func_eval_sprintf' 100 10 0.00025547
'func_eval_strjoin' 100 10 0.0011824
'func_sscanf_cellfun' 100 10 0.0023356
'func_sscanf_loop' 100 10 0.0012338
'func_sscanf_sprintf' 100 10 0.00031012
'func_sscanf_strjoin' 100 10 0.0011334
'func_str2num_cellfun' 100 10 0.002635
'func_str2num_loop' 100 10 0.0028056
'func_str2num_sprintf' 100 10 0.00027899
'func_str2num_strjoin' 100 10 0.0012117
'func_textscan_cellfun' 100 10 0.0029546
'func_textscan_loop' 100 10 0.0018652
'func_textscan_sprintf' 100 10 0.00028506
'func_textscan_strjoin' 100 10 0.001125
'solution_bsxfun_bytestream_Divakar' 100 10 0.00040027
'solution_bsxfun_bytestream_gpu_Divakar' 100 10 0.0032536
'solution_bsxfun_cumsum_Divakar' 100 10 0.00041019
'solution_bsxfun_sprintf_Divakar' 100 10 0.00031089
'solution_bsxfun_sprintf_gpu_Divakar' 100 10 0.0026271
'solution_eval_loops_CSTLink' 100 10 0.0012294
'solution_loops_CSTLink' 100 10 0.00033501
'solution_mex_Amro' 100 10 0.00027069
'solution_mex_chappjc' 100 10 0.00010682
'solution_mex_omp_Amro' 100 10 0.00039385
'solution_mex_omp_chappjc' 100 10 0.00015232
'solution_sscanf_Divakar' 100 10 0.0010108
'solution_sscanf_char_LuisMendo' 100 10 0.00050153
'solution_textscan_sprintf_chappjc' 100 10 0.00026958
'func_eval_cellfun' 1000 10 0.0092491
'func_eval_loop' 1000 10 0.016145
'func_eval_sprintf' 1000 10 0.067573
'func_eval_strjoin' 1000 10 0.070024
'func_sscanf_cellfun' 1000 10 0.020954
'func_sscanf_loop' 1000 10 0.011224
'func_sscanf_sprintf' 1000 10 0.0022546
'func_sscanf_strjoin' 1000 10 0.0058568
'func_str2num_cellfun' 1000 10 0.024699
'func_str2num_loop' 1000 10 0.02645
'func_str2num_sprintf' 1000 10 0.05713
'func_str2num_strjoin' 1000 10 0.060093
'func_textscan_cellfun' 1000 10 0.02592
'func_textscan_loop' 1000 10 0.017589
'func_textscan_sprintf' 1000 10 0.0020249
'func_textscan_strjoin' 1000 10 0.0055364
'solution_bsxfun_bytestream_Divakar' 1000 10 0.0018817
'solution_bsxfun_bytestream_gpu_Divakar' 1000 10 0.0066003
'solution_bsxfun_cumsum_Divakar' 1000 10 0.001982
'solution_bsxfun_sprintf_Divakar' 1000 10 0.0015578
'solution_bsxfun_sprintf_gpu_Divakar' 1000 10 0.0046952
'solution_eval_loops_CSTLink' 1000 10 0.011481
'solution_loops_CSTLink' 1000 10 0.0027254
'solution_mex_Amro' 1000 10 0.0022698
'solution_mex_chappjc' 1000 10 0.0006967
'solution_mex_omp_Amro' 1000 10 0.0015025
'solution_mex_omp_chappjc' 1000 10 0.00041463
'solution_sscanf_Divakar' 1000 10 0.0093785
'solution_sscanf_char_LuisMendo' 1000 10 0.0038031
'solution_textscan_sprintf_chappjc' 1000 10 0.0020323
'func_eval_cellfun' 10000 10 0.083676
'func_eval_loop' 10000 10 0.098798
'func_eval_sprintf' 10000 10 0.60429
'func_eval_strjoin' 10000 10 0.63656
'func_sscanf_cellfun' 10000 10 0.20675
'func_sscanf_loop' 10000 10 0.1088
'func_sscanf_sprintf' 10000 10 0.021725
'func_sscanf_strjoin' 10000 10 0.052341
'func_str2num_cellfun' 10000 10 0.24192
'func_str2num_loop' 10000 10 0.26538
'func_str2num_sprintf' 10000 10 0.53451
'func_str2num_strjoin' 10000 10 0.56759
'func_textscan_cellfun' 10000 10 0.25474
'func_textscan_loop' 10000 10 0.17402
'func_textscan_sprintf' 10000 10 0.018799
'func_textscan_strjoin' 10000 10 0.04965
'solution_bsxfun_bytestream_Divakar' 10000 10 0.019165
'solution_bsxfun_bytestream_gpu_Divakar' 10000 10 0.031283
'solution_bsxfun_cumsum_Divakar' 10000 10 0.027986
'solution_bsxfun_sprintf_Divakar' 10000 10 0.017761
'solution_bsxfun_sprintf_gpu_Divakar' 10000 10 0.024821
'solution_eval_loops_CSTLink' 10000 10 0.10885
'solution_loops_CSTLink' 10000 10 0.025136
'solution_mex_Amro' 10000 10 0.021374
'solution_mex_chappjc' 10000 10 0.0060774
'solution_mex_omp_Amro' 10000 10 0.0076461
'solution_mex_omp_chappjc' 10000 10 0.002058
'solution_sscanf_Divakar' 10000 10 0.10503
'solution_sscanf_char_LuisMendo' 10000 10 0.035483
'solution_textscan_sprintf_chappjc' 10000 10 0.018772
'func_eval_cellfun' 1e+05 10 0.85115
'func_eval_loop' 1e+05 10 0.97977
'func_eval_sprintf' 1e+05 10 6.2422
'func_eval_strjoin' 1e+05 10 6.5012
'func_sscanf_cellfun' 1e+05 10 2.0761
'func_sscanf_loop' 1e+05 10 1.0865
'func_sscanf_sprintf' 1e+05 10 0.22618
'func_sscanf_strjoin' 1e+05 10 0.53146
'func_str2num_cellfun' 1e+05 10 2.4041
'func_str2num_loop' 1e+05 10 2.6431
'func_str2num_sprintf' 1e+05 10 5.4
'func_str2num_strjoin' 1e+05 10 5.6967
'func_textscan_cellfun' 1e+05 10 2.5696
'func_textscan_loop' 1e+05 10 1.7175
'func_textscan_sprintf' 1e+05 10 0.19759
'func_textscan_strjoin' 1e+05 10 0.50314
'solution_bsxfun_bytestream_Divakar' 1e+05 10 0.21884
'solution_bsxfun_bytestream_gpu_Divakar' 1e+05 10 0.23607
'solution_bsxfun_cumsum_Divakar' 1e+05 10 0.29511
'solution_bsxfun_sprintf_Divakar' 1e+05 10 0.19882
'solution_bsxfun_sprintf_gpu_Divakar' 1e+05 10 0.17923
'solution_eval_loops_CSTLink' 1e+05 10 1.0943
'solution_loops_CSTLink' 1e+05 10 0.2534
'solution_mex_Amro' 1e+05 10 0.21575
'solution_mex_chappjc' 1e+05 10 0.060666
'solution_mex_omp_Amro' 1e+05 10 0.072168
'solution_mex_omp_chappjc' 1e+05 10 0.024385
'solution_sscanf_Divakar' 1e+05 10 1.0992
'solution_sscanf_char_LuisMendo' 1e+05 10 0.36688
'solution_textscan_sprintf_chappjc' 1e+05 10 0.19755
【讨论】:
哈哈,我明白了,您似乎对基准测试不感兴趣... :-) 好吧:1)regexp
很慢,2) str2num
实际上是 eval
在一个包装器,3) sscanf
和 textscan
实际上是快速 imo,但是多次调用它们会产生开销;作为交换,必须为大输入字符串构建大格式字符串。 4) str2double
可能很有趣,我没试过。希望这不是对帖子的 DOS 攻击。 :-)
@CST-Link str2double
可能是最糟糕的选择。这是我的第一次尝试,真的很慢。
@CST-Link:我并不是说这些都很快(事实上regexp
和str2double
慢得像狗!)。 sscanf
和 textscan
确实是最快的,其次是基于eval
的(达到一定大小)。我的意思是展示各种方法,这似乎是您正在寻找的...如果您想要性能,请走 C/C++ 之路:)
@Amro 感谢您查看解决方案的范围,并特别对sprintf
/textscan
方法进行了一些调整。 MATLAB 似乎很容易优化临时变量。 :) 我没有发现使用Delimiter
的优势,但尝试了两种方式(显然,它在我的 cmets 中),但你发现它对你的机器有帮助吗?如果当我在我的机器上运行你的测试台(非常好,顺便说一句)时它看起来一样,我会类似地调整我的官方解决方案。
@chappjc:这里也一样,我看不出设置delimiter
与否有太大区别。【参考方案4】:
基准测试
在用于此基准测试的基准代码上必须注意一些事项。这与带有这些更改的 Amro 的基准代码相同 -
1000
的除法已添加到所有解决方案中,以保持解决方案之间的一致性。
删除了有效性错误检查。无论如何,它不会影响运行时结果。
数据大小(行数)扩展到1000000
,为此我必须将每个单元格中的数字计数保持为8
,以将所有内容都存储到内存中。此外,我认为这将在矢量化解决方案和循环解决方案之间进行公平比较。
避免使用基于 mex 的解决方案,但我也希望看到与这些解决方案的运行时比较,如果有人过于友好而无法包含本文中包含的解决方案
与那些基于 mex 的基准进行基准测试。
对于 GPU 代码的基准测试,gputimeit
已使用,而不是 timeit
。
代码已打包并可用here。其中,使用bench_script.m
作为主函数。
基准测试结果
func nrows ncols time
__________________________ _____ _____ ________
'all_loops' 10000 8 0.026996
'char_and_sscanf_solution' 10000 8 0.034492
'eval_and_loops_solution' 10000 8 0.22548
'extIntsSprintfTextScan' 10000 8 0.036889
'func_eval_cellfun' 10000 8 0.16483
'func_eval_loop' 10000 8 0.1845
'func_sscanf_cellfun' 10000 8 0.40217
'func_sscanf_loop' 10000 8 0.19508
'func_sscanf_sprintf' 10000 8 0.027505
'func_sscanf_strjoin' 10000 8 0.11128
'func_str2num_cellfun' 10000 8 0.4976
'func_str2num_loop' 10000 8 0.5303
'func_textscan_cellfun' 10000 8 0.547
'func_textscan_loop' 10000 8 0.3752
'func_textscan_sprintf' 10000 8 0.036699
'func_textscan_strjoin' 10000 8 0.12059
'single_sscanf_solution' 10000 8 0.17122
'soln_bytstrm_bsxfun_gpu' 10000 8 0.023365
'soln_sprintf_bsxfun_gpu' 10000 8 0.019986
'solution_bytstrm_bsxfun' 10000 8 0.031165
'solution_cumsum_bsxfun' 10000 8 0.047445
'solution_sprintf_bsxfun' 10000 8 0.028417
'all_loops' 50000 8 0.13444
'char_and_sscanf_solution' 50000 8 0.1753
'eval_and_loops_solution' 50000 8 1.1242
'extIntsSprintfTextScan' 50000 8 0.1871
'func_eval_cellfun' 50000 8 0.82261
'func_eval_loop' 50000 8 0.91632
'func_sscanf_cellfun' 50000 8 2.0088
'func_sscanf_loop' 50000 8 0.97656
'func_sscanf_sprintf' 50000 8 0.13891
'func_sscanf_strjoin' 50000 8 0.56368
'func_str2num_cellfun' 50000 8 2.4786
'func_str2num_loop' 50000 8 2.6377
'func_textscan_cellfun' 50000 8 2.7452
'func_textscan_loop' 50000 8 1.8249
'func_textscan_sprintf' 50000 8 0.18556
'func_textscan_strjoin' 50000 8 0.60935
'single_sscanf_solution' 50000 8 0.90871
'soln_bytstrm_bsxfun_gpu' 50000 8 0.10591
'soln_sprintf_bsxfun_gpu' 50000 8 0.079611
'solution_bytstrm_bsxfun' 50000 8 0.18875
'solution_cumsum_bsxfun' 50000 8 0.27233
'solution_sprintf_bsxfun' 50000 8 0.16467
'all_loops' 80000 8 0.21602
'char_and_sscanf_solution' 80000 8 0.27855
'eval_and_loops_solution' 80000 8 1.7997
'extIntsSprintfTextScan' 80000 8 0.29733
'func_eval_cellfun' 80000 8 1.3171
'func_eval_loop' 80000 8 1.4647
'func_sscanf_cellfun' 80000 8 3.2232
'func_sscanf_loop' 80000 8 1.5664
'func_sscanf_sprintf' 80000 8 0.22136
'func_sscanf_strjoin' 80000 8 0.89605
'func_str2num_cellfun' 80000 8 3.9688
'func_str2num_loop' 80000 8 4.2199
'func_textscan_cellfun' 80000 8 4.3841
'func_textscan_loop' 80000 8 2.9181
'func_textscan_sprintf' 80000 8 0.29494
'func_textscan_strjoin' 80000 8 0.97383
'single_sscanf_solution' 80000 8 1.4542
'soln_bytstrm_bsxfun_gpu' 80000 8 0.15528
'soln_sprintf_bsxfun_gpu' 80000 8 0.11911
'solution_bytstrm_bsxfun' 80000 8 0.28552
'solution_cumsum_bsxfun' 80000 8 0.43238
'solution_sprintf_bsxfun' 80000 8 0.24801
'all_loops' 1e+05 8 0.26833
'char_and_sscanf_solution' 1e+05 8 0.34617
'eval_and_loops_solution' 1e+05 8 2.2465
'extIntsSprintfTextScan' 1e+05 8 0.37322
'func_eval_cellfun' 1e+05 8 1.641
'func_eval_loop' 1e+05 8 1.8339
'func_sscanf_cellfun' 1e+05 8 4.024
'func_sscanf_loop' 1e+05 8 1.9598
'func_sscanf_sprintf' 1e+05 8 0.27558
'func_sscanf_strjoin' 1e+05 8 1.1193
'func_str2num_cellfun' 1e+05 8 4.9592
'func_str2num_loop' 1e+05 8 5.2634
'func_textscan_cellfun' 1e+05 8 5.6636
'func_textscan_loop' 1e+05 8 3.981
'func_textscan_sprintf' 1e+05 8 0.36947
'func_textscan_strjoin' 1e+05 8 1.208
'single_sscanf_solution' 1e+05 8 1.8665
'soln_bytstrm_bsxfun_gpu' 1e+05 8 0.19389
'soln_sprintf_bsxfun_gpu' 1e+05 8 0.14588
'solution_bytstrm_bsxfun' 1e+05 8 0.35404
'solution_cumsum_bsxfun' 1e+05 8 0.54906
'solution_sprintf_bsxfun' 1e+05 8 0.30324
系统配置
MATLAB Version: 8.3.0.532 (R2014a)
Operating System: Ubuntu 14.04 LTS 64-bit
RAM: 4GB
CPU Model: Intel® Pentium® Processor E5400 (2M Cache, 2.70 GHz)
GPU Model: GTX 750Ti 2GB
configinfo.m
输出(仅重要的)-
MATLAB accelerator enabled
MATLAB JIT: enabled
MATLAB assertions: disabled
MATLAB Desktop: enabled
Java JVM: enabled
CPU: x86 Family 6 Model 23 Stepping 10, GenuineIntel
Number of processors: 2
CPU speed is: 2700 MHz
RAM: 4046992 kB
Swap space: 9760764 kB
Number of cores: 2
Number of threads: 2
【讨论】:
【参考方案5】:方法#1
广义上的第一种方法有两个部分。第一个给我们一个大长字符串,其中包含单元格数组中的所有字符,下划线分隔
两个单元格,本质上是用cumsum
实现的。第二个基于bsxfun
以所需格式为我们提供所需的数字输出。
代码看起来像这样 -
function out = solution_cumsum_bsxfun(list_of_words)
N = 1 + sum(list_of_words1=='_'); %// Number of "words" per cell
lens = cellfun('length',list_of_words); %// No. of chars [lengths] in each cell
tlens = sum(lens); %// Total number of characters [total of lengths]
cumlens = cumsum(lens); %// Cumulative lengths
%// Create an array of ones and zeros.
%// The length of this array would be equal to sum of characters in all cells.
%// The ones would be at the positions where new cells start, zeros otherwise
startpos_newcell(1,tlens)=0;
startpos_newcell(cumlens(1:end-1)+1)=1;
%// Calculate new positions for all characters in the cell array with
%// places/positions left between two cells for putting underscores
newpos = (1:tlens) + cumsum(startpos_newcell);
%// Create one long string that has all the characters from the cell arary
%// with underscores separating cells
one_str(newpos) = [list_of_words:];
one_str(cumlens + [1:numel(cumlens)]') = '_'; %//'#
pos_us = find(one_str=='_'); %// positions of underscores
wordend_idx = pos_us - [1:numel(pos_us)]; %// word end indices
wordlens = [wordend_idx(1) diff(wordend_idx)]; %// word lengths
max_wordlens = max(wordlens); %// maximum word length
%// Create mask where digit characters are to be placed
mask = bsxfun(@ge,[1:max_wordlens]',[max_wordlens+1-wordlens]); %//'#
%// Create a numeric array with columns for each word and each row holding a digit.
%// The most significant digits to least significant going from top to bottom
num1(max_wordlens,size(mask,2)) = 0;
num1(mask) = one_str(one_str~='_') - 48;
%// Finally get the desired output converting each column to a single
%// number, reshaping to desired size and scaling down by a factor of 1000
out = reshape(10.^(max_wordlens-4:-1:-3)*num1,N,[]).'; %//'#
return;
方法 #2
这是方法#1 的轻微变化,因为它使用sprintf
完成第一部分的工作。现在,这个想法是在我看到它在行动之后想到的
@chappjc's solution。感谢他让我在这个修改后的版本中使用它。
这是代码 -
function out = solution_sprintf_bsxfun(list_of_words)
N = 1 + sum(list_of_words1=='_'); %// Number of "words" per cell
%// Create one long string that has all the characters from the cell arary
%// with underscores separating cells
one_str = sprintf('%s_',list_of_words:);
pos_us = find(one_str=='_'); %// positions of underscores
wordend_idx = pos_us - [1:numel(pos_us)]; %// word end indices
wordlens = [wordend_idx(1) diff(wordend_idx)]; %// word lengths
max_wordlens = max(wordlens); %// maximum word length
%// Create mask where digit characters are to be placed
mask = bsxfun(@ge,[1:max_wordlens]',[max_wordlens+1-wordlens]); %//'#
%// Create a numeric array with columns for each word and each row holding a digit.
%// The most significant digits to least significant going from top to bottom
num1(max_wordlens,size(mask,2)) = 0;
num1(mask) = one_str(one_str~='_') - 48;
%// Finally get the desired output converting each column to a single
%// number, reshaping to desired size and scaling down by a factor of 1000
out = reshape(10.^(max_wordlens-4:-1:-3)*num1,N,[]).'; %//'#
return;
方法#3
这是一种基于getByteStreamFromArray
的全新方法-
function out = solution_bytstrm_bsxfun(list_of_words)
allchars = [list_of_words:];
digits_array = getByteStreamFromArray(allchars);
%// At my 32-bit system getByteStreamFromArray gets the significant digits
%// from index 65 onwards. Thus, we need to crop/index it accordingly
digits_array = digits_array(65:65+numel(allchars)-1) - 48;
% --------------------------------------------------------------------
N = 1 + sum(list_of_words1=='_'); %// Number of "words" per cell
lens = cellfun('length',list_of_words); %// No. of chars [lengths] in each cell
cumlens = cumsum(lens)'; %//'# Cumulative lengths
% ----------------------------------------------------------
pos_us = find(digits_array==47);
starts = [[1 cumlens(1:end-1)+1];reshape(pos_us+1,N-1,[])];
ends = [reshape(pos_us-1,N-1,[]) ; cumlens];
gaps = ends(:) - starts(:) + 1;
maxg = max(gaps);
mask1 = bsxfun(@lt,maxg-gaps',[1:maxg]');
num_array(maxg,numel(lens)*N)=0;
num_array(mask1) = digits_array(digits_array~=47);
out = reshape(10.^(maxg-4:-1:-3)*num_array,N,[]).';
return;
下面列出了上述方法的 GPU 版本。
方法#4
function out = soln_sprintf_bsxfun_gpu(list_of_words)
N = 1 + sum(list_of_words1=='_'); %// Number of "words" per cell
%// Create one long string that has all the characters from the cell arary
%// with underscores separating cells. Now this one uses sprintf as
%// firstly proposed in @chappjc's solution and he has agreed to let me use
%// it, so appreciating his help on this.
digits_array = gpuArray(single(sprintf('%s_',list_of_words:)));
digits_array = digits_array - 48;
mask_us = digits_array==47; %// mask of underscores
pos_us = find(mask_us);
wordend_idx = pos_us - gpuArray.colon(1,numel(pos_us)); %// word end indices
wordlens = [wordend_idx(1) diff(wordend_idx)]; %// word lengths
max_wordlens = max(wordlens); %// maximum word length
%// Create a numeric array with columns for each word and each row holding a digit.
%// The most significant digits to least significant going from top to bottom
num1 = single(zeros(max_wordlens,numel(pos_us),'gpuArray')); %//'
num1(bsxfun(@ge,gpuArray.colon(1,max_wordlens)',...
max_wordlens+1-single(wordlens))) = digits_array(~mask_us); %//'
%// Finally get the desired output converting each column to a single
%// number, reshaping to desired size and scaling down by a factor of 1000
outg = reshape(10.^(max_wordlens-4:-1:-3)*num1,N,[]).'; %//'#
out = gather(outg);
返回;
方法#5
function out = soln_bytstrm_bsxfun_gpu(list_of_words)
us_ascii_num = 95;
allchars = [list_of_words:];
%// At my 32-bit system getByteStreamFromArray gets the significant digits
%// from index 65 onwards. Thus, we need to crop/index it accordingly
alldigits = getByteStreamFromArray(allchars);
digits_array1 = gpuArray(alldigits(65:65+numel(allchars)-1));
% --------------------------------------------------------------------
lens = cellfun('length',list_of_words); %// No. of chars [lengths] in each cell
N = sum(digits_array1(1:lens(1))==us_ascii_num)+1; %// Number of "words" per cell
lens = gpuArray(lens);
cumlens = cumsum(lens)'; %//'# Cumulative lengths
% ----------------------------------------------------------
mask_us = digits_array1==us_ascii_num; %// mask of underscores
pos_us = find(mask_us);
starts = [[1 cumlens(1:end-1)+1];reshape(pos_us+1,N-1,[])];
ends = [reshape(pos_us-1,N-1,[]) ; cumlens];
gaps = ends(:) - starts(:) + 1;
maxg = max(gaps);
mask1 = bsxfun(@lt,maxg-gaps',[1:maxg]');
num_array = zeros(maxg,numel(lens)*N,'gpuArray'); %//'
num_array(mask1) = digits_array1(~mask_us)-48;
out = reshape(10.^(maxg-4:-1:-3)*num_array,N,[]).'; %//'
out = gather(out);
return;
【讨论】:
在我的机器上差别更大:eval = 1.478, sscanf = 0.3423 @chappjc 我相信这是因为内存不足bsxfun
因为我在我的低 RAM 系统上将其拉伸得太远了。不过,感谢您执行测试!
@Divakar 我在发布代码中添加了用于生成测试用例、使用timeit
的计时功能和标准测试脚本。我希望他们没事;一旦我得到你的接受,我希望你不介意我删除你提议的基于 timeit
的旧脚本。
@CST-Link 确保继续删除它。所以,referee_timing.m
是替代它的那个,对吧?
恭喜!你赢了!【参考方案6】:
我将sprintf
与comma-separated list 输入一起应用以生成单个完全分隔的一维字符串(在每行/字符串的末尾带有分隔符),然后使用textscan
提取整数:
extractIntsSprintfTextScan.m
function M = extractIntsSprintfTextScan(list_of_words)
% optimized lines, see below for explanation of each op
M = textscan(sprintf('%s_',C:),'%.0f', 'Delimiter','_');
M = reshape(M1, [], numel(C)).';
% cell to string, adding _ suffix
% s = sprintf('%s_',list_of_words:);
% extract integers given _ as delimiter
% C = textscan(s,'%u','Delimiter','_','CollectOutput',1); % can also use %d for signed
% C = textscan(s,'%.0f_','CollectOutput',1); % a little faster to put _ in the pattern
% matrix form
% M = reshape(C1,[],numel(list_of_words)).'/1000; % remove transpose for faster output
end
基准(以前的计时现在过时了,请参阅 Amro's benchmark post 中的 sprintf
+textscan
解决方案)
机器
MATLAB R2014b 64 位 Windows 7 64 位 24 GB 内存 双 Xeon X5550(2.67GHZ,8 个物理内核)
更新:将输出类型从整数类型更改为双精度。重新运行基准测试。更新 2:将格式说明符从“%f”更改为“%.0f”,因为不需要小数,可能会加快扫描速度。增加 N = 100e3;
(参见 GitHub Gist)。更新 3:CST-Link 计时功能第 2 版的新基准(参见 新 GitHub Gist 与建议使用 referee_timing.m)。更新 4:添加 Luis 的解决方案。请注意,由于数据是随机生成的,因此结果会有所波动。
更新 5:优化调整 - 请参阅 Amro 的基准测试帖子。
【讨论】:
+1 哇,你的机器真棒!你能澄清一下你用的是哪个N
吗?或者甚至尝试不同的N
值?我对N
有一个奇怪的变化。最后,人们可能不知道“乔恩”是谁 :-)
@LuisMendo 这是一个有趣的小玩具!我只是将 N 从 6e4 更改为 1e5(有关实际脚本,请参阅 GitHub Gist)。我有一种感觉,因为我有这么多 RAM 和内核,其他基准测试可能不会强调某些代码方面。
@chappjc 嗨,我添加了新的基准测试代码。我希望它将使测试更容易和更公平。希望您不会介意分享您对此的看法。
@CST-Link 我已经运行了你的新基准函数(非常好的设置!)并将test script here。你真的应该使用正式的赏金系统来提供赏金,30 天也有点长,我不是因为我喜欢我的结果才这么说。 :)
我的机器对于大N
的部分解决方案有问题,所以无法比较。您介意将我更正后的解决方案添加到您的测试中吗?【参考方案7】:
(最新提案)
这种方式消除了我的反 MATLAB 解决方案的最后一个 MATLAB 特定部分;纯字节扫描,循环中循环,内存分配的东西:
%'all_loops.m'
function array_of_numbers = all_loops(list_of_words)
%'Precalculate important numbers'
n_numbers = 1 + sum(list_of_words1=='_');
n_words = numel(list_of_words);
%'Allocate memory'
array_of_numbers = zeros(n_numbers, n_words);
slice_of_numbers = zeros(n_numbers, 1);
%'Loop trough chunks of cell array'
for k = 1:n_words
str = list_of_wordsk;
pos_max = size(str, 2);
value = 0;
index = 1;
for pos = 1:pos_max
if str(pos) == '_'
slice_of_numbers(index) = value;
value = 0;
index = index + 1;
else
value = 10*value + (str(pos) - '0');
end;
slice_of_numbers(index) = value; %'almost forgot'
end;
array_of_numbers(:, k) = slice_of_numbers;
end;
%'This is a waste of memory, but is kind of required'
array_of_numbers = transpose(array_of_numbers / 1000);
end
(基准测试) 对于如下配置(取自configinfo.m)
MATLAB configuration information CPU: x86 Family 6 Model 58 Stepping 9, GenuineIntel
This data was gathered on: 24-Oct-2014 14:19:31 The measured CPU speed is: 2600 MHz
MATLAB version: 7.14.0.739 (R2012a) Number of processors: 4
MATLAB root: xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx RAM: 3289 MB
MATLAB accelerator enabled Swap space: 6576 MB
MATLAB JIT: enabled Microsoft Windows 7
MATLAB assertions: disabled Number of cores: 2
MATLAB Desktop: enabled Number of threads: 2
获得以下结果(第 3 次运行):
Generating 10000-words test case...
Timing 1000000-words test case...
approach4: Error - Out of memory. Type HELP MEMORY for your options.
approach1: Error - Out of memory. Type HELP MEMORY for your options.
single_sscanf_solution: Error - Out of memory. Type HELP MEMORY for your options.
char_and_sscanf_solution: 5.076296[s]
extractIntsSprintfTextScan: 4.328066[s]
all_loops: 1.795730[s]
eval_and_loops_solution: 10.027541[s]
Generating 100000-words test case...
Timing 100000-words test case...
approach4: 0.252107[s]
approach1: 0.370727[s]
single_sscanf_solution: 1.364936[s]
char_and_sscanf_solution: 0.515599[s]
extractIntsSprintfTextScan: 0.444586[s]
all_loops: 0.179575[s]
eval_and_loops_solution: 1.010240[s]
Generating 10000-words test case...
Timing 10000-words test case...
approach4: 0.026642[s]
approach1: 0.039550[s]
single_sscanf_solution: 0.136711[s]
char_and_sscanf_solution: 0.049708[s]
extractIntsSprintfTextScan: 0.042608[s]
all_loops: 0.017636[s]
eval_and_loops_solution: 0.099111[s]
您可能会注意到最后一步的差异,“Generating 10000...”和“Timing 1000000...”之间;为了减少占用的内存(否则在生成测试用例时一切都会失败)我使用repmat(referee_test_case(1e4), 100, 1)
而不是referee_test_case(1e6)
。
【讨论】:
您能在我的解决方案中添加approach4.m
的基准吗?给您添麻烦了! :)
@Divakar Timing on approach4.m
添加。您还会发布自己的计时测试吗?我会很好奇。
嗯,我对当前基于 timeit 的基准测试持怀疑态度,因为它在进入下一个方法之前没有清除一种方法的输出。内存不足错误很明显。所以,我创建了一个不同的基准测试代码,它与基于我的系统配置的结果一起上传到这里 - pastebin.com/1EucNwYB 你觉得怎么样?
@Divakar 抱歉,我在工作,pastebin 已过滤。我不确定是否要清除,因为所有数据都是在函数工作区中创建的。一旦函数退出,所有包含的变量都被释放。你想改变脚本的顺序,把你的函数放在第一位,然后把内存密集型放在第一位吗?像这样,清除不会有问题(因为没有必要,我是原始的)。请告诉我您喜欢哪种顺序,我会安排的。
@Divakar 另外,很高兴看到代码在您的系统上的执行情况。 :-)【参考方案8】:
接受挑战!这是迄今为止我最快的实现,是的,它是一个 C++ MEX 文件 :)
solution_mex.cpp
#include "mex.h"
#include <vector>
#include <string>
#include <sstream>
#include <algorithm>
#include <iterator>
namespace
// get i-th string from cell-array of strings
std::string getString(const mxArray *cellstr, mwIndex idx)
mxArray *arr = mxGetCell(cellstr, idx);
if (arr == NULL) mexErrMsgIdAndTxt("mex:err", "null/uninitialized");
if (!mxIsChar(arr)) mexErrMsgIdAndTxt("mex:err", "not a string");
char *cstr = mxArrayToString(arr);
if (cstr == NULL) mexErrMsgIdAndTxt("mex:err", "null");
std::string str(cstr);
mxFree(cstr);
return str;
// count of numbers in char-delimited string
mwSize count_numbers(const std::string &s)
return std::count(s.begin(), s.end(), '_') + 1;
// parse numbers
template <typename T>
void parseNumbers(const mxArray *cellstr, const mwSize len, std::vector<T> &v)
// cell-array of strings
std::vector<std::string> vs;
vs.reserve(len);
for (mwIndex idx=0; idx<len; ++idx)
vs.push_back(getString(cellstr, idx));
// join vector of strings into one
std::stringstream ss;
std::copy(vs.begin(), vs.end(),
std::ostream_iterator<std::string>(ss, "_"));
// split string into numbers (separated by single-char delimiter)
T num;
while (ss >> num)
v.push_back(num);
ss.ignore(1);
;
void mexFunction(int nlhs, mxArray *plhs[], int nrhs, const mxArray *prhs[])
// validate inputs
if (nrhs!=1 || nlhs>1) mexErrMsgIdAndTxt("mex:err", "wrong num args");
if (!mxIsCell(prhs[0])) mexErrMsgIdAndTxt("mex:err", "not cell");
// allocate output matrix
mwSize len = mxGetNumberOfElements(prhs[0]);
mwSize sz = (len > 0) ? count_numbers(getString(prhs[0],0)) : 0;
plhs[0] = mxCreateNumericMatrix(sz, len, mxDOUBLE_CLASS, mxREAL);
if (plhs[0] == NULL) mexErrMsgIdAndTxt("mex:err", "null");
if (len == 0 || sz == 0) return;
// parse cell array into numbers
std::vector<int> v;
v.reserve(len*sz);
parseNumbers(prhs[0], len, v);
if (v.size() != (len*sz)) mexErrMsgIdAndTxt("mex:err", "wrong size");
// copy numbers into output matrix
std::copy(v.begin(), v.end(), mxGetPr(plhs[0]));
代码易于阅读;我在可能的情况下使用标准 STL 库(没有肮脏的技巧),而且有大量的检查和输入验证,所以只要你遵循问题中描述的输入格式,它就应该很健壮。
稍后我会更新一些基准测试,但现在您可以自己测试它并与其他解决方案进行比较...
请注意,上述 MEX 函数返回大小为 n_numbers * n_words
的矩阵,因此您需要转置结果。
这是一个包装器 M-function,您可以使用它在裁判程序下运行:
solution_mex.m
function array_of_numbers = mex_solution(list_of_words)
array_of_numbers = solution_mex(list_of_words).';
end
编辑#1
让我们稍微简化一下代码;此版本通过一次处理一个字符串并将结果直接放入输出矩阵来使用更少的内存:
solution_mex.cpp
#include "mex.h"
#include <string>
#include <sstream>
#include <algorithm>
namespace
std::string getString(const mxArray *cellstr, const mwIndex idx)
// get i-th string from cell-array of strings
mxArray *arr = mxGetCell(cellstr, idx);
if (!arr || !mxIsChar(arr)) mexErrMsgIdAndTxt("mex:err", "not a string");
char *cstr = mxArrayToString(arr);
if (cstr == NULL) mexErrMsgIdAndTxt("mex:err", "null");
std::string str(cstr);
mxFree(cstr);
return str;
mwSize count_numbers(const std::string &s)
// count of numbers in char-delimited string
return std::count(s.begin(), s.end(), '_') + 1;
;
void mexFunction(int nlhs, mxArray *plhs[], int nrhs, const mxArray *prhs[])
// validate inputs
if (nrhs!=1 || nlhs>1) mexErrMsgIdAndTxt("mex:err", "wrong num args");
if (!mxIsCell(prhs[0])) mexErrMsgIdAndTxt("mex:err", "not a cell");
// determine sizes
const mxArray *cellstr = prhs[0];
const mwSize n_words = mxGetNumberOfElements(cellstr);
const mwSize n_nums = (n_words > 0) ?
count_numbers(getString(cellstr,0)) : 0;
// allocate output matrix
plhs[0] = mxCreateDoubleMatrix(n_nums, n_words, mxREAL);
if (plhs[0] == NULL) mexErrMsgIdAndTxt("mex:err", "null");
if (n_words == 0 || n_nums == 0) return;
double *out = mxGetPr(plhs[0]);
// extract numbers from strings
for (mwIndex idx=0, i=0; idx<n_words; ++idx)
std::istringstream ss(getString(cellstr, idx));
int num;
while(ss >> num)
out[i++] = num;
ss.ignore(1);
编辑#2 (OpenMP)
下一步,让我们让它多线程;只需添加两行代码,我们就可以使用 OpenMP 隐式执行此操作!我正在对每个字符串进行并行化。
首先我们添加omp parallel for
pragma,然后我们将索引变量i
设为每个线程私有,这样线程就知道列的起始索引。
在edit#1的上一个代码中,只需将最后一个循环替换为:
// extract numbers from strings
#pragma omp parallel for
for (mwIndex idx=0; idx<n_words; ++idx)
mwIndex i = idx*n_nums; // starting index for i-th column
std::istringstream ss(getString(cellstr, idx));
int num;
while(ss >> num)
out[i++] = num;
ss.ignore(1);
我在 Windows x64 上运行 R2014a,并尝试使用 VS2013 和 MinGW-w64 GCC 4.9.1 进行编译。让我指出,GCC 编译的版本比目前所有的解决方案都要快:
% compile with MinGW-w64
>> mex -largeArrayDims -f mingwopts.bat solution_mex.cpp -output solution_mex_gcc
% set appropriate number of threads
>> setenv('OMP_NUM_THREADS','4');
% quick benchmark
>> timeit(@() solution_mex_gcc(repmat('02_04_04_52_23_14_54_672_0',1e6,1)).')
ans =
0.6658
【讨论】:
为了完整起见,您应该提供一个至少执行其余操作的 MATLAB 包装器(转置和除以 1000)。而且,这并不是我的想法。这就是我指定“MATLAB-native”的原因。 :-D 不过,欢迎您的回答。 @CST-Link 好吧,这开辟了一套全新的解决方案。我根据说明特别坚持使用内置...您真的要开始采用 MEX 解决方案吗? +1 对于伟大的代码,一如既往。 @Amro 好吧,在我提出的最后一个解决方案中,我将自己放弃 MATLAB:没有函数调用。或者可能是 4 个。:-) 但是不,为了公平起见,我想将赏金奖励授予 MATLAB 标准函数和语言代码。 @chappjc,CST-Link:我发布了一个新的多线程版本。告诉你,真的很快! @Divakar 不。就我而言,我们只是在搞砸。【参考方案9】:以下解决方案似乎有效:
function array_of_numbers = char_and_sscanf_solution(list_of_words)
s = char(list_of_words);
s(s==95) = 32; %// 95 is '_'; 32 is ' '
s = [ s repmat(32, numel(list_of_words), 1) ];
array_of_numbers = reshape(sscanf(s.','%i '), [], numel(list_of_words)).'/1000;
使用referee_timing.m
和referee_test_case.m
对结果进行基准测试:
Generating 10000-words test case...
Timing 10000-words test case...
eval_and_loops_solution: 0.190642[s]
single_sscanf_solution: 0.234413[s]
approach1: 0.073901[s]
char_and_sscanf_solution: 0.068311[s]
Generating 100000-words test case...
Timing 100000-words test case...
eval_and_loops_solution: 1.957728[s]
single_sscanf_solution: 2.426764[s]
approach1: 0.766020[s]
char_and_sscanf_solution: 0.706387[s]
Generating 1000000-words test case...
Timing 1000000-words test case...
eval_and_loops_solution: 18.975746[s]
char_and_sscanf_solution: 7.147229[s]
计算机信息:
Matlab R2010b Windows 7 家庭高级版 64 位 Service Pack 1 奔腾(R)双核 CPU T4300 2.10 GHz 4 GB 内存
【讨论】:
您好,我添加了一些基于timeit
生成测试用例和时序的代码,并提出了一个基准测试的标准脚本。我期待听到您的意见(这很好吗?不是吗?)。
非常干净的解决方案,顺便说一句。 +1
@CST-Link 我认为这很好......但它变得复杂了 :-) 无论如何,我觉得奇怪的是某些解决方案在我的系统上花费了这么长时间
我的想法如下:不是将所有内容都放在基础工作区中,而是每次运行都在自己的函数工作区中进行,该工作区在计时函数结束时被删除。另外,捕捉错误以便他们不会打扰其他时间是很容易的。 @Divakar 的一个好主意是生成不同的案例,而不是重新使用相同的字符串(这可能是不现实的)。很抱歉临时更改规则,我保证从现在开始我会小心的。
不同长度的随机行似乎干扰了您创建s
。由于char
用空格填充了不相等的行,所以_
的连接不在大多数行的最后一个数字之后。除了我的sprintf
技巧之外,我不确定有什么好的解决方法。 ;)以上是关于MATLAB 性能基准测试的主要内容,如果未能解决你的问题,请参考以下文章