C ++ for循环太慢

时间:2020-05-22 15:57:29

标签: c++ callback portaudio

我正在尝试使用PortAudio制作音频应用程序。我的回调函数非常慢,并且一直在产生持续的欠载。我一步一步地删除了回调中的所有内容,直到发现问题:for循环。我删除了所有内容,以便在回调函数中唯一发生的事情是for循环,并且仍然导致欠载。我知道这是for循环,因为当我减少迭代次数时,欠载就消失了。

library(tidyverse)

dataset_subset <- tribble(
  ~"Unit", ~"Group", ~"Feature1", ~"Feature2", ~"Feature3", ~"Feature4",
  1,         1,        "blue"   ,   "x",           "a",           "12",
  2,         1,        "yellow",    "y",           "b",           "15",
  3,         2,        "green"  ,   "x",           "a",           "13",
  4,         3,        "indigo",    "z",           "c",           "12",
  5,         1,        "green"  ,   "y",           "b",           "16")


dataset_subset %>% 
  pivot_longer(contains("Feature")) %>% 
  ggplot(aes(x = value)) +
  geom_bar(aes(y = ..prop.., group = name), stat = "count", fill = "#f68060", alpha =.6, width = .4) +
  scale_y_continuous(labels = scales::percent) +
  facet_wrap(~name, scales = "free_x")

这是我测试的完整代码:https://gist.github.com/johnroper100/b87641f5609dbb49bc3c1121b1f4daf1

这个问题并不是很必要,但是我在python等效设备(声音设备)中执行了相同的回调,并且没有问题。

5 个答案:

答案 0 :(得分:6)

From the API documentation

在流运行时,PortAudio会定期调用流回调。回调函数负责处理通过输入和输出参数传递的音频样本的缓冲区。

PortAudio流回调以很高的优先级或实时优先级运行。它必须始终满足其时间期限。不要从流回调中分配内存,访问文件系统,调用库函数或调用其他函数,这些回调可能会阻塞或花费不可预测的时间来完成。

为了使流保持无故障操作,回调必须消耗和返回音频数据,其速度要比记录和/或播放的速度快。 PortAudio预计每次回调调用可能会执行一段时间以流采样率接近frameCount音频帧的持续时间。合理地期望能够利用PortAudio回调中70%或更多的可用CPU时间。但是,由于缓冲区大小的调整和其他因素,并非所有的主机API都能在任意固定回调缓冲区大小的情况下,在繁重的CPU负载下保证音频稳定性。 当需要较高的回调CPU使用率时,可以通过将paFramesPerBufferUnspecified指定为Pa_OpenStream()framesPerBuffer参数来实现最可靠的行为。

我突出显示了相关部分。

答案 1 :(得分:3)

一个没有任何内容的for循环对于您的CPU来说仍然是艰苦的工作。

如果您不希望CPU将所有功能都花在该for循环上,则至少应使该for循环包含放弃CPU周期的内容。

最简单的方法是在其中调用sleep()或nanosleep()。

更高级的方法是使用具有适当机制的线程,使它们等待所需的操作。

答案 2 :(得分:2)

我认为文档(在提供的答案@ÁdámHunyadi中)解释了回调函数无法正常运行的原因。但是我不确定它是否解释为什么 for循环太慢。

为了使流保持无故障操作,回调 必须消耗和返回音频数据的速度快于记录和/或 。PortAudio预计每次回调调用都可能 执行的持续时间接近frameCount音频的持续时间 帧。合理的期望是 能够利用PortAudio中70%或更多的可用CPU时间 打回来。但是,由于缓冲区大小调整和其他因素, 所有主机API均能在CPU繁重的情况下保证音频稳定性 加载任意固定的回调缓冲区大小。 高回调时 需要CPU利用率,才能实现最可靠的行为 通过使用paFramesPerBufferUnspecified作为Pa_OpenStream() framesPerBuffer参数。

看一下PortAudio Doc Example中的示例,它们遍历for-loop迭代的framesPerBuffer(在文档中定义为framesCount)。 处理frameCount的持续时间。但是在您的代码示例中,您正在做一样的操作,但操作的大小却是vector 的大小(我相信这不是 frameCount的持续时间)。仅使用一个for-loop而不是两个,并且使用framesPerBuffer而不是10000作为最大迭代次数可能会解决“循环太慢” 问题

答案 3 :(得分:0)

除了可能会导致您的循环或整个程序变慢的特定于API的问题外,我还将为您的一般问题提供一般性建议。如果您忽略返回值,则始终偏爱前缀(++i;)而不是后缀(i++;),因为前缀不会返回您要在其上应用运算符的值/对象的副本。因此,可以避免不必要的复制操作。虽然,大多数现代编译器确实会为您动态替换它,但是它们可能会以某种方式被您的代码所欺骗,因此始终最好养成使用前缀递增的习惯。当然,您需要使用-O3参数来编译代码以获得最高性能,并确保编译器将尽其所能。要进一步优化循环的另一种选择是多线程。

答案 4 :(得分:0)

在patestCallback结束之后,可能会调用捕获新帧的代码,如果您无法按时完成任务,则意味着您的CPU功能不够强大(=处理过于复杂)。这不是(为什么)解释,而是一种解决方案,如果您的CPU中有多个Core(即使您拥有一个Core,有时它也可以工作),该解决方案应该可以工作,您可以创建一个处理音频的线程,回调应该仅将(或更改指针)复制到要处理的音频,这使您的主线程(音频捕获线程)不必等待太久。