【问题标题】:Slow cloud processing. How to use all CPU cores in grabber callback?云处理缓慢。如何在抓取回调中使用所有 CPU 内核?
【发布时间】:2019-07-18 12:51:10
【问题描述】:

我正在使用 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 的帧都可以。

【问题讨论】:

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


    【解决方案1】:

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

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

    • 使用-fno-omit-frame-pointer 标志或等效项进行编译
    • perf record -g &lt;executable&gt;
    • 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 这样的框架

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

    选项 2 适用于所需延迟较低且需要对帧进行排序的情况。但是,为了在 TaskFlow 中保持低延迟,您需要以 CPU 内核可以处理它们的速率来提供帧。否则,您可能会使 CPU 过载,没有空闲 RAM 导致页面抖动,实际上会降低性能

    这两个选项都要求您确保输出以正确的顺序到达。这可以使用promises 或丢弃乱序帧的队列来完成。通过实施这些解决方案的一部分或全部,您可以确保您的 CPU 利用率保持在较高水平,甚至可能达到 100%。


    为了了解最适合您的情况,了解您的目标性能指标是什么,智能地分析代码并无偏见地进行测试。

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

    【讨论】:

    • 感谢您的建议。太糟糕了 PCL 内部没有并行化(有一些例外)。我想我们将不得不考虑使用队列并在单独的线程中处理传入的帧。我们应该能够在 CPU 阻塞之前处理几个帧,希望这应该足够了。
    • 内部并行化是什么意思?从根本上讲,在不了解用例的情况下,添加并行 for 循环可能弊大于利。虽然将来可能会添加 C++14 风格的执行器来处理它。
    • 我同意并非所有事情都可以轻松并行化,但我认为许多例程都可以。迭代云点的例程。以 removeNAN 为例。有一堆 for 循环,可以很容易地并行执行。与直通过滤器、异常值删除等情况相同。还是我错了?
    猜你喜欢
    • 2020-01-07
    • 1970-01-01
    • 2016-05-31
    • 2018-12-15
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多