并发访问 GCD 中的单个 FFTSetup 数据结构

Posted

技术标签:

【中文标题】并发访问 GCD 中的单个 FFTSetup 数据结构【英文标题】:Concurrent access to a single FFTSetup data structure in GCD 【发布时间】:2012-07-07 20:00:34 【问题描述】:

是否可以创建一个单个 FFTSetup 数据结构并使用它同时执行多个 FFT 计算?会像下面这样工作吗?

FFTSetup fftSetup = vDSP_create_fftsetup(
                                         16,         // vDSP_Length __vDSP_log2n,
                                         kFFTRadix2  // FFTRadix __vDSP_radix
                                         );
NSAssert(fftSetup != NULL, @"vDSP_create_fftsetup() failed to allocate storage");

for (int i = 0; i < 100; i++)

  dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0),
  ^
    vDSP_fft_zrip(
                  fftSetup,              // FFTSetup __vDSP_setup,
                  &(splitComplex[i]),    // DSPSplitComplex *__vDSP_ioData,
                  1,                     // vDSP_Stride __vDSP_stride,
                  16,                    // vDSP_Length __vDSP_log2n,
                  kFFTDirection_Forward  // FFTDirection __vDSP_direction
                  );
  );

我认为答案取决于以下考虑:

1) vDSP_fft_zrip() 是否仅以“只读”方式访问fftSetup 内的数据(或它指向的数据)?或者fftSetup 中是否有一些临时缓冲区(暂存空间)由vDSP_fft_zrip() 在执行其 FFT 计算时写入?

2) 如果像fftSetup 这样的数据以“只读”方式访问,多个进程/线程/任务/块可以同时访问它吗? (我在考虑这样一种情况,即多个进程可以打开同一个文件进行读取,但不一定用于写入或追加。这个类比合适吗?)

在相关说明中,FFTSetup 数据结构占用了多少内存?有什么办法可以查到吗? (它是一种不透明的数据类型。)

【问题讨论】:

【参考方案1】:

您可以创建一个 FFT 设置并重复和同时使用它。这是预期用途。 (我是 vDSP_fft_zrip 的当前实现和 vDSP 中其他 FFT 实现的作者。)

【讨论】:

那么 OP 应该记录一个文档错误,因为预期用途不明确。 是的,请为任何不清楚的文档提交错误。软件工程师和文档编写者之间存在一些官僚作风。对于新的 vDSP_DFT* 例程,我们将一些文档直接放在头文件(/System/Library/Frameworks/Accelerate.framework/Frameworks/vecLib.framework/Headers/vDSP.h)中,并且明确说明使用多线程的 DFT 例程。但是,DFT 例程目前仅支持 3*2n 和 5*2n 的长度,因此它们还没有替代 FFT 例程。 DFT 例程也支持 3*5*2**n。 上面的评论,格式更正:是的,请为任何不清楚的文档提交错误。软件工程师和文档编写者之间存在一些官僚作风。对于新的 vDSP_DFT* 例程,我们将一些文档直接放在头文件(/System/Library/Frameworks/Accelerate.framework/Frameworks/vecLib.framework/Headers/vDSP.h)中,并且明确说明使用多线程的 DFT 例程。但是,DFT 例程目前仅支持 3*2^n 和 5*2^n 的长度,因此它们还没有替代 FFT 例程。 还有一个更新:2^n 支持已添加到 vDSP_DFT* 例程中,它们可以而且应该用来代替 vDSP_FFT* 例程。【参考方案2】:

在Using Fourier Transforms 中,我们被告知FFTSetup 包含FFT 权重数组,它是一系列复指数。 vDSP_create_fftsetup documentation 说

一旦准备好,设置结构可以被 FFT 重复使用 函数(读取结构中的数据并且不改变它) 对于任何(2 的幂)长度,不超过您创建时指定的长度 结构。

所以

    从概念上讲,vDSP_fft_zrip 不需要修改权重数组,因此它似乎是不改变 FFTSetup 的 FFT 函数之一(我还没有看到任何 除了创建/销毁),但是不能保证实际的实现是做什么的——它可以做任何事情。

    if vDSP_fft_zrip 真正以只读方式访问其FFTSetup,那么可以从多个线程中执行此操作。

至于内存使用,FFT 权重数组是 e^i*k*2*M_PI/N for k = [0..N-1],这是 N 个复数浮点值,所以 2* N*sizeof(float)。

但是那些复杂的指数非常对称,所以谁知道呢,在底层实现可能需要更少的内存。或者更多!

在您的情况下,N = 2^16,因此看到最多使用 256k 也就不足为奇了。

这会让你在哪里?我认为FFTSetup 可以从多个线程访问似乎是合理的,但它似乎没有记录。你可能很幸运。或者现在或在框架的未来版本中不幸和令人不快的惊喜。

那么……你觉得幸运吗?

【讨论】:

vDSP_fft_zrip 不使用您描述的权重数组。有几个数组,用于 FFT 的不同部分。一些数组以方便实现的顺序提供权重,一些可能提供位反转排列中使用的索引。从 Intel 到 ARM,甚至从 Intel 32 位到 Intel 64 位,设置可能有所不同,Apple 可能会不时更改它。您不能期望使用任何特定数量的空间。 (我写了代码。)【参考方案3】:

为此,我不会尝试与 vDSP 函数或 Accelerate 框架(其中 vDSP 是其中的一部分)中的任何其他函数进行任何显式并发。为什么?因为 Accelerate 已经被设计为利用多个内核以及给定处理器实现的特定细微差别,所以代表您 - 请参阅 http://developer.apple.com/library/mac/#DOCUMENTATION/Darwin/Reference/ManPages/man7/vecLib.7.html。您最终可能会重新并行化实现内部的已经并行计算(如果不是现在,那么可能在以后的版本中)。 Accelerate 框架的最佳方法通常是假设它比您更聪明,并以最简单的方式使用它,然后进行性能测量。如果这些测量反映的性能水平在某种程度上不足以满足您的需求,那么请尝试您自己的优化(和/或在http://bugreport.apple.com 提交针对 Accelerate 框架的错误报告,因为该框架的作者总是有兴趣知道在哪里或如果他们的努力不符合开发人员的要求)。

【讨论】:

Accelerate 的 vImage 部分将工作分配给多个处理器,给定足够大小的作业,除非被要求不这样做。但是,vDSP_fft_zrip 位于 Accelerate 的 vecLib 部分,并以单线程方式运行。 当然,这就是为什么我提出“如果不是现在,那么可能在以后的版本中”的附带条件 - 从长远来看,开发人员最好天真地使用这些 API,让 Apple 自己的工程师找到热点并根据需要应用优化(无论是多核还是“其他”),而不是让后来的实现与开发人员自己优化性能的尝试发生冲突。 编写代码来调用当前的单线程例程并希望它们以后变为多线程并不是一种有用的技术,尤其是对于那些希望他们的软件现在变得优秀的人来说。如果一个应用程序将受益于 vDSP 例程的多线程,我建议在应用程序中实现多线程并将 VECLIB_MAXIMUM_THREADS 环境变量设置为 1 以防止 vDSP 将来干扰多线程。

以上是关于并发访问 GCD 中的单个 FFTSetup 数据结构的主要内容,如果未能解决你的问题,请参考以下文章

GCD 中的并发队列与串行队列

GCD编程-串行队列与并发队列

IOS GCD(线程的 串行并发 基本使用)

IIS / .Net 仅允许对给定用户会话的单个并发响应

iOS中GCD的使用小结

ios多线程操作—— GCD串行队列与并发队列