MATLAB 中的 FFT 与 DFT 运行时间比较(复杂性分析)

信息处理 matlab fft 自由度 在家工作 编码
2021-12-24 23:31:55

所以我们得到了一个任务来绘制 MATLAB 的 FFT 算法和我在 MATLAB.G 中编写的 DFT 算法所花费的时间

预期的输出应该是遵循 O(n^2) 复杂度的 DFT 算法,但是当我绘制输出时它是线性的。

我的 DFT 实现是否存在问题或导致它的其他原因?

第一张图是计算 dft vs N(N point fft) 的时间,而另一张图是我实现 DFT vs N 的时间。

在此处输入图像描述

timedft = zeros(1,length(2:1024));
timefft = zeros(1,length(2:1024));
%DFT
sig = rand(1,1024);
for N = 2:1024
    tic;
    opdft = zeros(1,N);
    for i = 0:N-1
        for l = 0:length(sig)-1
            temp = sig(1,l+1).*exp(-2*pi*l*i*j/N);
            opdft(1,i+1)=opdft(1,i+1)+temp;
        end
    end
    timedft(1,N-1) = toc;
    tic;
    fft(rand,N);
    timefft(1,N-1) = toc;
end
figure;
subplot(211);
plot(2:1024,timedft);
title("fftvsdft");
xlabel("N");
ylabel("Time")
subplot(212);
plot(2:1024,timefft);
xlabel("N");
ylabel("Time");
2个回答

Abhinav Jain,欢迎来到 DSP 社区。

我为您构建了运行时比较的适当测试。

关于 MATLAB 计时的一些提示:

  1. 永远不要花时间在脚本中。总是调用一个函数来完成繁重的工作。当您从脚本运行某些内容时,它会在全局范围内运行,这意味着 MATLAB 无法像在函数中那样优化它。
  2. 对函数计时时,对测量进行几次迭代。我喜欢取这个迭代的中位数。其他人喜欢最低限度。通常平均值不好,因为它对异常值很敏感。
  3. MATLAB 是一种基于 JIT 的脚本语言。这意味着任何事情的第一次运行都需要更多时间。因此(2)是至关重要的。
  4. 注意 FFT / DFT 的结果很复杂。因此,当您为复杂数组分配内存时,您应该使用 - vArrayName = complex(zeros(arrayLength, 1));
  5. 当您需要 MATLAB 中的虚数时,您应该使用or (不需要乘法)。当然你也可以使用但是无需乘以,因为它可以帮助 JIT 引擎理解您的意思是虚数而不是变量。i=11i1j5i

结果如下:

在此处输入图像描述

如您所见,确实 DFT 实现的运行时间表现为O(n^2).

代码如下:

%% Simulation Parameters

vNumSamples     = 2:2:1024;
numIterations   = 6;


%% Generate Data

mDftTime = zeros(numIterations, length(vNumSamples));
mFftTime = zeros(numIterations, length(vNumSamples));

for jj = 1:length(vNumSamples)
    numSamples = vNumSamples(jj);
    vX = randn(numSamples, 1);

    for ii = 1:numIterations
        hDftTimer           = tic();
        vXDft               = ApplyDft(vX, numSamples);
        mDftTime(ii, jj)    = toc(hDftTimer);

        hFftTimer           = tic();
        vXFft               = fft(vX);
        mFftTime(ii, jj)    = toc(hFftTimer);
    end

end


%% Run Time Analysis

vDftMedian = median(mDftTime).';
vFftMedian = median(mFftTime).';

vDftMean = mean(mDftTime).';
vFftMean = mean(mFftTime).';

vDftMax = max(mDftTime).';
vFftMax = max(mFftTime).';

vDftMin = min(mDftTime).';
vFftMin = min(mFftTime).';


%% Display Results

figureIdx = figureIdx + 1;

hFigure = figure('Position', figPosLarge);
hAxes   = subplot(4, 1, 1);
% set(hAxes, 'NextPlot', 'add');
hLineSeries = plot(vNumSamples, [vDftMedian, vFftMedian]);
set(hLineSeries, 'LineWidth', lineWidthNormal);
% set(hLineSeries(2), 'LineStyle', ':');
set(get(hAxes, 'Title'), 'String', {['DFT vs. FFT Run Time - Median']}, ...
    'FontSize', fontSizeTitle);
set(get(hAxes, 'XLabel'), 'String', {['Input Size']}, ...
    'FontSize', fontSizeAxis);
set(get(hAxes, 'YLabel'), 'String', {['Run Time [Sec]']}, ...
    'FontSize', fontSizeAxis);
hLegend = ClickableLegend({['DFT'], ['FFT']});

hAxes   = subplot(4, 1, 2);
% set(hAxes, 'NextPlot', 'add');
hLineSeries = plot(vNumSamples, [vDftMean, vFftMean]);
set(hLineSeries, 'LineWidth', lineWidthNormal);
% set(hLineSeries(2), 'LineStyle', ':');
set(get(hAxes, 'Title'), 'String', {['DFT vs. FFT Run Time - Mean']}, ...
    'FontSize', fontSizeTitle);
set(get(hAxes, 'XLabel'), 'String', {['Input Size']}, ...
    'FontSize', fontSizeAxis);
set(get(hAxes, 'YLabel'), 'String', {['Run Time [Sec]']}, ...
    'FontSize', fontSizeAxis);
hLegend = ClickableLegend({['DFT'], ['FFT']});

hAxes   = subplot(4, 1, 3);
% set(hAxes, 'NextPlot', 'add');
hLineSeries = plot(vNumSamples, [vDftMax, vFftMax]);
set(hLineSeries, 'LineWidth', lineWidthNormal);
% set(hLineSeries(2), 'LineStyle', ':');
set(get(hAxes, 'Title'), 'String', {['DFT vs. FFT Run Time - Max']}, ...
    'FontSize', fontSizeTitle);
set(get(hAxes, 'XLabel'), 'String', {['Input Size']}, ...
    'FontSize', fontSizeAxis);
set(get(hAxes, 'YLabel'), 'String', {['Run Time [Sec]']}, ...
    'FontSize', fontSizeAxis);
hLegend = ClickableLegend({['DFT'], ['FFT']});

hAxes   = subplot(4, 1, 4);
% set(hAxes, 'NextPlot', 'add');
hLineSeries = plot(vNumSamples, [vDftMin, vFftMin]);
set(hLineSeries, 'LineWidth', lineWidthNormal);
% set(hLineSeries(2), 'LineStyle', ':');
set(get(hAxes, 'Title'), 'String', {['DFT vs. FFT Run Time - Min']}, ...
    'FontSize', fontSizeTitle);
set(get(hAxes, 'XLabel'), 'String', {['Input Size']}, ...
    'FontSize', fontSizeAxis);
set(get(hAxes, 'YLabel'), 'String', {['Run Time [Sec]']}, ...
    'FontSize', fontSizeAxis);
hLegend = ClickableLegend({['DFT'], ['FFT']});

if(generateFigures == ON)
    saveas(hFigure,['Figure', num2str(figureIdx, figureCounterSpec), '.png']);
end

正如您在上面的代码中看到的,我numIterations = 6为每个样本数量运行每个算法。
然后,在时间数组上mDftTime/mFftTime我可以分析 Median / Mean / Min / Max 运行时间。
这是在 MATLAB 中分析函数运行时间的正确方法。

完整代码可在我的StackExchange Signal Processing Q51516 GitHub 存储库中找到。

备注
您正在 DSP 世界中迈出第一步,成为现代工程师的很大一部分是关于编程代码。
你应该花时间学习如何正确地编写代码和风格,以便你和你的合作伙伴容易理解和管理。
尝试从其他人的例子中学习。

大多数 FFT 实现使用预先计算复指数项的设置步骤,并且在传统复杂度计算中省略了该步骤。

MATLAB 的解释器通常在 mfile 第一次运行时运行较慢。

MATLAB 多年来一直将 fftw 用于其 fft 例程。它可能在使用 fftw 之前使用了 fft pack。fftw 有它自己的先前初始化,也称为“智慧”,它不应该成为你的基准测试的一部分。

您可以通过 fftw() 调用访问 fftw 规划器。

所以本质上,需要消除初始化。

您可以使用 dftmtx 预先计算指数并消除您的 for 循环。

在更深层次上,现代多线程处理器的性能很难根据失败次数来预测运行时间。MATLAB 最初会返回触发器,但该功能已被删除。

有光速

https://github.com/tminka/lightspeed

这会返回一些失败的能力。您需要从源代码编译它。这可能有助于在您的基准测试中使用触发器。MATLAB 还有一个很好的分析器,您可以使用它来调整您的代码。我猜你会看到大部分时间都在计算 exp()。

即使您考虑了初始化,您也可能不会得到您期望的结果。我可以保证,只要您没有内存不足,8086 理论就会与测量结果一致。

fft 适用于双精度和单精度数字。single 在许多处理器上更快。