【问题标题】:slower to mix logical variables with double?将逻辑变量与双精度混合更慢?
【发布时间】:2013-05-07 18:59:49
【问题描述】:

我有 0-1 个值的向量,我需要对其进行一些矩阵运算。它们不是很稀疏(只有一半的值是 0),但将它们保存为逻辑变量而不是 double 可以节省 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指令集的限制吗?

编辑:我的系统是 Mac OS X 10.8.3 上的 MATLAB R2012b,Intel Core i7 3.4 GHz

EDIT2:一些 cmets 表明,这只是 Mac OS X 的一个问题。如果可能的话,我想编译来自不同架构和操作系统的结果。

EDIT3:我的实际问题需要计算所有可能的长度为m 的二进制向量的很大一部分,其中m 可能太大而8 * m * 2^m 无法放入内存。

【问题讨论】:

  • 我认为这与 MATLAB 在乘以 A 之前隐式转换 logicaldouble 的事实有关。
  • 这里的时间不同:0.161891 / 0.057331 / 0.049061 秒。
  • R2013a Vista32 Intel Core Duo T9300:0.216960 / 0.026960 / 0.081925 秒。
  • 我可以用一个非常相似的系统 (0.018691 / 0.025646 / 0.036361) 确认您的时间安排:Matlab R2012b、Mac OS X 10.8.3、Intel Core i7 (Retina MBP)。我已经多次看到这个确切的问题,并且长期以来一直怀疑 Matlab 在优化 OS X 中的逻辑计算方面做得不好。天真的事情是 Matlab 将数据重新转换为更高级别的双精度,Matlab 的本机格式,但是显然有更聪明的方案。在某些情况下,函数double 可用于在计算之前手动重铸部分或全部逻辑数据 - 在上面的简单示例中,这将无济于事。
  • 您可能还想向 The MathWorks 提交 bug report / service request 并引用此页面。

标签: performance matlab types floating-point matrix-multiplication


【解决方案1】:

我将首先发布一个稍微好一点的基准。我正在使用 Steve Eddins 的 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 比两者都慢(因为它的重点是有效的内存使用而不是速度)。


现在请注意,针对您的平台优化的 BLAS 实现上的 MATLAB relies 可以执行 full-matrix multiplication(想想 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】:

当您处理完全适合缓存且不太稀疏的数据时(如在您的基准测试中),做额外的工作(如在逻辑类型和双精度之间转换,或使用稀疏存储方案)以尝试减少内存占用只会减慢您的代码速度(正如您所注意到的)。

当每个加载的数据元素完成足够多的计算工作时(如您的示例中的情况),来自 L1 缓存的数据访问速度足够快,可以“有效地免费”。发生这种情况时,执行速度受到计算的限制,而不是加载/存储流量;通过使用逻辑变量,您正在执行 更多 计算,这会减慢您的基准测试速度。

您真正想要解决的问题的工作集有多大?如果它至少不大于处理器上的 L2 缓存,则应该只使用普通的双精度矩阵。使用逻辑变量变得有利的确切阈值可能要大得多,但需要一些实验来确定。 (这也将取决于 MATLAB 如何处理转换;您希望将转换作为乘法平铺的一部分进行 - 如果 MATLAB 不这样做,它可能永远不会比使用 double 更快,不不管数据集有多大)。

【讨论】:

  • 感谢您的回答。在所有情况下,我的问题都不适用于 L2 缓存。它应该是一个用于拟合概率模型的通用工具箱,所以我不会提前知道问题有多大,或者用户的 L1/2 缓存会有多大。从其他 cmets 看来,它似乎高度依赖于操作系统或架构,这是这种优化的另一个问题。
【解决方案3】:

我认为结果合理地与不同的表示相关。

非稀疏双精度数组可以简单而有效地表示一小部分数据,这些数据很容易放入缓存中。

逻辑数组更节省空间,每个元素只使用一个字节而不是 8 个字节,但是当您只有 8 个元素时,这不会带来任何好处。另一方面,在使用它进行双精度运算之前,它确实必须转换为双精度,添加一个步骤。

稀疏数组使用更复杂的表示,旨在在大多数数组为零时节省空间。它需要更多的操作来确定给定索引处的元素为零,或者获得其非零值。将它用于即使是最小的缓存也很容易适应的 50% 非零数组是一种误用。它最适合降低几乎为零的大型阵列的内存和数据传输成本。见Sparse vs Normal Array Matlab

如果你真的在处理 8 个元素的数组,你应该坚持使用非稀疏的 double 数组。如果您的实际工作涉及更大的数组,则需要以相似的大小进行基准测试。您还需要确保您的测试数据的稀疏性与真实数据相匹配。

【讨论】:

  • 感谢您的回答。我的问题不是超级稀疏(略超过 50% 的零)而且相当大。我正在考虑大量长度大于 100 的二进制向量。在我的计算机上通常需要几分钟,但我想确保它对每个人来说都不会太慢。
  • 我认为你的下一步应该是一个更现实的基准。使用真实的尺寸、向量的数量以及向量的长度。您当前的测试测量了对非常短向量的重复访问,这可能低估了使用 double 对内存占用的影响。
  • 是的。感谢您的建议。
猜你喜欢
  • 2016-01-15
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2016-11-08
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2022-01-26
相关资源
最近更新 更多