缓慢的云处理。如何在采集器回调中使用所有CPU内核?

时间:2019-07-16 11:14:12

标签: c++ visual-studio-2017 point-cloud-library

我正在使用Kinect2Grabber,并且想进行一些实时处理,但是我得到的性能约为1 fps。最终,我想估计一个空中掷球的轨迹。我无法通过如此缓慢的处理来做到这一点:(

我在AMD Ryzen Threadripper 1900X 8核处理器上使用Windows 10 64位,Visual Studio 2017,PCL 1.9.1多合一安装程序MSVC2017 x64。我的项目中启用了OpenMP,优化也启用了。但是,当我运行程序时,其CPU使用率约为12-13%。我在做什么错了?

int main(int argc, char* argv[])
{
    boost::shared_ptr<visualization::PCLVisualizer> viewer(new visualization::PCLVisualizer("Point Cloud Viewer"));
    viewer->setCameraPosition(0.0, 0.0, -1.0, 0.0, 0.0, 0.0); 

    PointCloud<PointType>::Ptr cloud(new PointCloud<PointType>);

    // Retrieved Point Cloud Callback Function
    boost::mutex mutex;
    boost::function<void(const PointCloud<PointType>::ConstPtr&)> function = [&cloud, &mutex](const PointCloud<PointType>::ConstPtr& ptr) {
        boost::mutex::scoped_lock lock(mutex);

        //Point Cloud Processing 
        cloud = ptr->makeShared();
        std::vector<int> indices;
        removeNaNFromPointCloud<PointType>(*cloud, *cloud, indices);
        pass_filter(0.5, 0.90, cloud);
        outlier_removal(50, 1.0, cloud);
        downsampling_vox_grid(0.005f, cloud);
        normals(0.04, cloud, cloud_normals);
        segmentation(cloud, cloud_normals);
    };

    boost::shared_ptr<Grabber> grabber = boost::make_shared<Kinect2Grabber>();
    boost::signals2::connection connection = grabber->registerCallback(function);
    grabber->start();

    while (!viewer->wasStopped()) {
        // Update Viewer
        viewer->spinOnce();
        boost::mutex::scoped_try_lock lock(mutex);
        if (lock.owns_lock() && cloud) {
            // Update Point Cloud
            if (!viewer->updatePointCloud(cloud, "chmura")) {
                viewer->addPointCloud(cloud, "chmura");
            }
        }
    }

    grabber->stop();

    // Disconnect Callback Function
    if (connection.connected()) {
        connection.disconnect();
    }
    return 0;
}

pass_filter,outlier_removal等省略的代码直接从教程中获取,并且可以正常工作,但是从outlier_removal(含)开始非常慢。 您的帮助将不胜感激。

我不必使用Kinect2Grabber。在Windows上从Kinec2抓取和处理帧,一切都会很好。

1 个答案:

答案 0 :(得分:0)

我看到一些缓解您的问题的方法。 Threadripper的12-13%的使用率听起来不错(100/16约为〜6.25%),因为这意味着完全使用了1个物理核心(1个线程用于IO,1个用于计算?只是我的猜测)。

要获得更好的性能,您需要进行概要分析以了解造成瓶颈的原因。 Perf是实现此目的的绝佳工具。有一个关于how to profile code by Chandler的精彩视频。这不是perf教程的最佳位置,但是 TL; DW

  • 使用-fno-omit-frame-pointer标志或等效标志进行编译
  • perf record -g <executable>
  • perf report -g了解哪些功能占用最多的CPU周期
  • perf report -g 'graph,0.5,caller'了解哪些调用路径占用最多的CPU周期

最有可能发现的问题是

  • 重复创建诸如VoxelGrid之类的一次性对象:一次实例化对象可以更好地利用CPU周期
  • grabber的锁和IO

这将为您提供更高的帧速率,但仍将CPU利用率限制为12-13%,即单线程限制。


由于您使用的是ThreadRipper,因此可能会使用线程来使用其他CPU并分离IO,计算和可视化

  • 用于抓取器的一个线程,用于抓取帧并将其推入队列
  • 另一个线程根据CPU可用性使用队列中的帧。这会将计算出的数据保存到另一个队列中
  • 可视化线程,它从输出队列中获取数据

这使您可以调整队列大小以丢弃帧以改善延迟。根据您的自定义Kinect2Grabber的设计,可能不需要这样做。在您分析代码后,这一点将显而易见。

这有可能通过将CPU利用率提高到20%(因为抓取器和可视化线程将在全速状态下工作)来显着减少延迟并提高帧率。


为了充分利用所有线程,使用者线程可以将帧卸载到其他线程,以允许CPU一次处理多个帧。为此,您可以采用以下选项(它们不是唯一的。您可以将选项2用作选项1的主力军,以获取全部利益)

  1. 要一次并行执行多个独立功能(例如,您的主力lambda),请查看boost::thread_poolboost::thread_group
  2. 对于基于管道的模型(每个阶段都在不同的线程中运行),您可以使用TaskFlow
  3. 之类的框架。

当您不打算使用诸如延迟之类的特定指标时,选项1是合适的。它还需要对代码进行最少的更改,但是需要在高延迟(上面指出当前的设计问题)或为每个线程创建一个对象副本之间进行权衡,以防止每次设置。

选项2适用于所需的等待时间很短并且需要订购帧的情况。但是,为了保持TaskFlow中的低延迟,您需要以CPU内核可以处理它们的速率来提供帧。否则,您可以使CPU过载,不留任何可用RAM导致页面抖动,并实际上降低性能

这两个选项都要求您确保输出以正确的顺序到达。可以使用promises或掉落故障帧的队列来完成。通过实施部分或全部解决方案,您可以确保CPU利用率保持很高,甚至100%。


为了知道最适合您的情况,知道您的目标性能指标是什么,智能地配置代码并进行正确测试。

  • 没有指标,即使不需要,您也可以进入提高性能的深渊(例如:5 fps可能足以代替60 fps)
  • 如果不进行测量,您将无法确定自己是否获得了正确的结果,或者是否确实在攻克瓶颈。
  • 没有公正的测试,您可能会得出错误的结论。