Linux测试处理fps为30、1920*1080、一分钟的视频性能

news2025/1/19 11:21:20

前置条件

模拟fps为30、1920*1080、一分钟的视频

项目CMakeLists.txt

cmake_minimum_required(VERSION 3.30)
project(testOpenGl)

set(CMAKE_CXX_STANDARD 11)

add_executable(testOpenGl main.cpp
        testOpenCl.cpp
        testOpenCl.h
        TestCpp.cpp
        TestCpp.h
        TestCppThread.cpp
        TestCppThread.h
        TestSIMD.cpp
        TestSIMD.h)

# 查找OpenCL
find_package(OpenCL REQUIRED)

# 链接OpenCl库
target_include_directories(testOpenGl PRIVATE ${OpenCL_INCLUDE_DIRS})
target_link_libraries(testOpenGl PRIVATE ${OpenCL_LIBRARIES})

# 检测SIMD支持并添加编译选项
include(CheckCXXCompilerFlag)

check_cxx_compiler_flag("-mavx" COMPILER_SUPPORTS_AVX)
check_cxx_compiler_flag("-mavx2" COMPILER_SUPPORTS_AVX2)

if(COMPILER_SUPPORTS_AVX2)
    target_compile_options(testOpenGl PRIVATE -mavx2)
elseif (COMPILER_SUPPORTS_AVX)
    target_compile_options(testOpenGl PRIVATE -mavx)
else ()
    message(FATAL_ERROR "AVX or AVX2 is not supported by compiler")
endif ()

C++代码

//
// Created by lai on 2025/1/17.
//

#include "TestCpp.h"

#include <iostream>
#include <vector>
#include <random>
#include <chrono>

// 灰度转换函数
void to_gray(const std::vector<unsigned char>& input, std::vector<unsigned char>& output, int width, int height) {
    for (int i = 0; i < width * height; ++i) {
        int offset = i * 3;  // RGB 分量
        unsigned char r = input[offset];
        unsigned char g = input[offset + 1];
        unsigned char b = input[offset + 2];
        // 灰度公式
        output[i] = static_cast<unsigned char>(0.299f * r + 0.587f * g + 0.114f * b);
    }
}
void TestCpp::runTest() {
    const int width = 1920;         // 视频宽度
    const int height = 1080;        // 视频高度
    const int fps = 30;             // 帧率
    const int duration = 60;        // 视频持续时间(秒)
    const int frameCount = fps * duration; // 总帧数

    // 模拟视频帧数据:随机生成每帧的 RGB 数据
    std::vector<unsigned char> inputFrame(width * height * 3);
    std::vector<unsigned char> outputFrame(width * height);

    std::random_device rd;
    std::mt19937 gen(rd());
    std::uniform_int_distribution<> dis(0, 255);

    // 开始处理
    auto startTime = std::chrono::high_resolution_clock::now();

    for (int frame = 0; frame < frameCount; ++frame) {
        // 随机生成模拟的 RGB 数据
        for (auto& pixel : inputFrame) {
            pixel = dis(gen);
        }

        // 调用灰度转换函数
        to_gray(inputFrame, outputFrame, width, height);

        // 打印进度
        if (frame % 30 == 0) {
            std::cout << "Processed frame: " << frame + 1 << "/" << frameCount << std::endl;
        }
    }

    auto endTime = std::chrono::high_resolution_clock::now();
    double elapsedTime = std::chrono::duration<double>(endTime - startTime).count();

    // 打印处理时间
    std::cout << "Processed " << frameCount << " frames in " << elapsedTime << " seconds." << std::endl;
    std::cout << "Average time per frame: " << (elapsedTime / frameCount) << " seconds." << std::endl;

}

C++多线程

//
// Created by lai on 2025/1/17.
//

#include "TestCppThread.h"


#include <iostream>
#include <vector>
#include <random>
#include <chrono>
#include <thread>

// 灰度转换函数,每个线程处理一部分图像
void to_gray_chunk(const std::vector<unsigned char>& input, std::vector<unsigned char>& output, int width, int height, int start, int end) {
    for (int i = start; i < end; ++i) {
        int offset = i * 3;  // RGB 分量
        unsigned char r = input[offset];
        unsigned char g = input[offset + 1];
        unsigned char b = input[offset + 2];
        // 灰度公式
        output[i] = static_cast<unsigned char>(0.299f * r + 0.587f * g + 0.114f * b);
    }
}

void TestCppThread::runTest() {
    const int width = 1920;         // 视频宽度
    const int height = 1080;        // 视频高度
    const int fps = 30;             // 帧率
    const int duration = 60;        // 视频持续时间(秒)
    const int frameCount = fps * duration; // 总帧数
    const int numThreads = std::thread::hardware_concurrency(); // 获取可用线程数

    // 模拟视频帧数据:随机生成每帧的 RGB 数据
    std::vector<unsigned char> inputFrame(width * height * 3);
    std::vector<unsigned char> outputFrame(width * height);

    std::random_device rd;
    std::mt19937 gen(rd());
    std::uniform_int_distribution<> dis(0, 255);

    // 开始处理
    auto startTime = std::chrono::high_resolution_clock::now();

    for (int frame = 0; frame < frameCount; ++frame) {
        // 随机生成模拟的 RGB 数据
        for (auto& pixel : inputFrame) {
            pixel = dis(gen);
        }

        // 启动多个线程来处理图像
        std::vector<std::thread> threads;
        int chunkSize = width * height / numThreads; // 每个线程处理的像素块大小
        for (int t = 0; t < numThreads; ++t) {
            int start = t * chunkSize;
            int end = (t == numThreads - 1) ? (width * height) : (start + chunkSize); // 最后一个线程处理剩余的像素
            threads.emplace_back(to_gray_chunk, std::cref(inputFrame), std::ref(outputFrame), width, height, start, end);
        }

        // 等待所有线程完成
        for (auto& t : threads) {
            t.join();
        }

        // 打印进度
        if (frame % 30 == 0) {
            std::cout << "Processed frame: " << frame + 1 << "/" << frameCount << std::endl;
        }
    }

    auto endTime = std::chrono::high_resolution_clock::now();
    double elapsedTime = std::chrono::duration<double>(endTime - startTime).count();

    // 打印处理时间
    std::cout << "Processed " << frameCount << " frames in " << elapsedTime << " seconds." << std::endl;
    std::cout << "Average time per frame: " << (elapsedTime / frameCount) << " seconds." << std::endl;

}

CPU版本的Opencl

cmake中添加

# 查找OpenCL
find_package(OpenCL REQUIRED)

# 链接OpenCl库
target_include_directories(testOpenGl PRIVATE ${OpenCL_INCLUDE_DIRS})
target_link_libraries(testOpenGl PRIVATE ${OpenCL_LIBRARIES})

测试代码

//
// Created by lai on 2025/1/16.
//
#include "testOpenCl.h"

#include <chrono>
#include <CL/cl.h>
#include <iostream>
#include <vector>
#include <random>

// OpenCL 内核代码
const char* kernelSource = R"(
__kernel void to_gray(
    __global unsigned char* input,
    __global unsigned char* output,
    const int width,
    const int height)
{
    int id = get_global_id(0);  // 每个线程处理一个像素
    if (id < width * height) {
        int offset = id * 3;  // RGB 分量
        unsigned char r = input[offset];
        unsigned char g = input[offset + 1];
        unsigned char b = input[offset + 2];
        // 灰度公式
        output[id] = (unsigned char)(0.299f * r + 0.587f * g + 0.114f * b);
    }
}
)";
void TestOpenCl::runTests() {
    const int width = 1920;         // 视频宽度
    const int height = 1080;        // 视频高度
    const int fps = 30;             // 帧率
    const int duration = 60;        // 视频持续时间(秒)
    const int frameCount = fps * duration; // 总帧数

    // 模拟视频帧数据:随机生成每帧的 RGB 数据
    std::vector<unsigned char> inputFrame(width * height * 3);
    std::vector<unsigned char> outputFrame(width * height);

    std::random_device rd;
    std::mt19937 gen(rd());
    std::uniform_int_distribution<> dis(0, 255);

    // 初始化 OpenCL
    cl_int err;
    cl_platform_id platform;
    clGetPlatformIDs(1, &platform, nullptr);

    cl_device_id device;
    clGetDeviceIDs(platform, CL_DEVICE_TYPE_CPU, 1, &device, nullptr);

    cl_context context = clCreateContext(nullptr, 1, &device, nullptr, nullptr, &err);
    cl_command_queue queue = clCreateCommandQueue(context, device, 0, &err);

    cl_program program = clCreateProgramWithSource(context, 1, &kernelSource, nullptr, &err);
    clBuildProgram(program, 1, &device, nullptr, nullptr, nullptr);

    cl_kernel kernel = clCreateKernel(program, "to_gray", &err);

    // 创建 OpenCL 缓冲区
    cl_mem inputBuffer = clCreateBuffer(context, CL_MEM_READ_ONLY, inputFrame.size(), nullptr, &err);
    cl_mem outputBuffer = clCreateBuffer(context, CL_MEM_WRITE_ONLY, outputFrame.size(), nullptr, &err);

    // 开始处理
    auto startTime = std::chrono::high_resolution_clock::now();

    for (int frame = 0; frame < frameCount; ++frame) {
        // 随机生成模拟的 RGB 数据
        for (auto& pixel : inputFrame) {
            pixel = dis(gen);
        }

        // 写入数据到 OpenCL 缓冲区
        clEnqueueWriteBuffer(queue, inputBuffer, CL_TRUE, 0, inputFrame.size(), inputFrame.data(), 0, nullptr, nullptr);

        // 设置内核参数
        clSetKernelArg(kernel, 0, sizeof(cl_mem), &inputBuffer);
        clSetKernelArg(kernel, 1, sizeof(cl_mem), &outputBuffer);
        clSetKernelArg(kernel, 2, sizeof(int), &width);
        clSetKernelArg(kernel, 3, sizeof(int), &height);

        // 定义工作区大小
        size_t globalSize = width * height;

        // 执行内核
        clEnqueueNDRangeKernel(queue, kernel, 1, nullptr, &globalSize, nullptr, 0, nullptr, nullptr);

        // 读取处理后的灰度数据
        clEnqueueReadBuffer(queue, outputBuffer, CL_TRUE, 0, outputFrame.size(), outputFrame.data(), 0, nullptr, nullptr);

        // 打印进度
        if (frame % 30 == 0) {
            std::cout << "Processed frame: " << frame + 1 << "/" << frameCount << std::endl;
        }
    }

    auto endTime = std::chrono::high_resolution_clock::now();
    double elapsedTime = std::chrono::duration<double>(endTime - startTime).count();

    // 打印处理时间
    std::cout << "Processed " << frameCount << " frames in " << elapsedTime << " seconds." << std::endl;
    std::cout << "Average time per frame: " << (elapsedTime / frameCount) << " seconds." << std::endl;

    // 释放 OpenCL 资源
    clReleaseMemObject(inputBuffer);
    clReleaseMemObject(outputBuffer);
    clReleaseKernel(kernel);
    clReleaseProgram(program);
    clReleaseCommandQueue(queue);
    clReleaseContext(context);
}

内存对齐的SIMD指令集

cmake添加

# 检测SIMD支持并添加编译选项
include(CheckCXXCompilerFlag)

check_cxx_compiler_flag("-mavx" COMPILER_SUPPORTS_AVX)
check_cxx_compiler_flag("-mavx2" COMPILER_SUPPORTS_AVX2)

if(COMPILER_SUPPORTS_AVX2)
    target_compile_options(testOpenGl PRIVATE -mavx2)
elseif (COMPILER_SUPPORTS_AVX)
    target_compile_options(testOpenGl PRIVATE -mavx)
else ()
    message(FATAL_ERROR "AVX or AVX2 is not supported by compiler")
endif ()
//
// Created by lai on 2025/1/17.
//

#include "TestSIMD.h"

#include <iostream>
#include <vector>
#include <random>
#include <chrono>
#include <immintrin.h> // SIMD 指令集
#include <cstdlib>  // 用于posix_memalign

void to_gray_simd(const unsigned char* input, unsigned char* output, int width, int height) {
    const int pixelCount = width * height;
    const __m256 scale_r = _mm256_set1_ps(0.299f); // 红色通道的权重
    const __m256 scale_g = _mm256_set1_ps(0.587f); // 绿色通道的权重
    const __m256 scale_b = _mm256_set1_ps(0.114f); // 蓝色通道的权重

    int i = 0;
    for (; i <= pixelCount - 8; i += 8) {
        // 加载 8 组 RGB 像素
        __m256i pixel_r = _mm256_loadu_si256((__m256i*)&input[i * 3]);  // 确保内存对齐
        __m256i pixel_g = _mm256_loadu_si256((__m256i*)&input[i * 3 + 1]);
        __m256i pixel_b = _mm256_loadu_si256((__m256i*)&input[i * 3 + 2]);

        // 转换为浮点数以便计算
        __m256 r_f = _mm256_cvtepi32_ps(pixel_r);
        __m256 g_f = _mm256_cvtepi32_ps(pixel_g);
        __m256 b_f = _mm256_cvtepi32_ps(pixel_b);

        // 灰度转换公式
        __m256 gray_f = _mm256_add_ps(
            _mm256_add_ps(_mm256_mul_ps(r_f, scale_r), _mm256_mul_ps(g_f, scale_g)),
            _mm256_mul_ps(b_f, scale_b));

        // 转回整数
        __m256i gray_i = _mm256_cvtps_epi32(gray_f);

        // 存储结果
        _mm256_storeu_si256((__m256i*)&output[i], gray_i);
    }

    // 处理剩余像素(非对齐部分)
    for (; i < pixelCount; ++i) {
        int offset = i * 3;
        unsigned char r = input[offset];
        unsigned char g = input[offset + 1];
        unsigned char b = input[offset + 2];
        output[i] = static_cast<unsigned char>(0.299f * r + 0.587f * g + 0.114f * b);
    }
}

void TestSIMD::runTest() {
    const int width = 1920;         // 视频宽度
    const int height = 1080;        // 视频高度
    const int fps = 30;             // 帧率
    const int duration = 60;        // 视频持续时间(秒)
    const int frameCount = fps * duration; // 总帧数
    size_t size = width * height * 3 * sizeof(unsigned char);

    // 模拟视频帧数据:随机生成每帧的 RGB 数据
    // 使用posix_memalign分配对齐内存
    unsigned char* inputFrame;
    unsigned char* outputFrame;
    int alignment = 32; // 使用32字节对齐
    int resultInput = posix_memalign((void**)&inputFrame, alignment, size);
    int resultOutput = posix_memalign((void**)&outputFrame, alignment, size);
    if (resultInput != 0 || resultOutput != 0) {
        std::cerr << "memory allocation failed" << std::endl;
        return;
    }


    std::random_device rd;
    std::mt19937 gen(rd());
    std::uniform_int_distribution<> dis(0, 255);

    // 开始处理
    auto startTime = std::chrono::high_resolution_clock::now();

    for (int frame = 0; frame < frameCount; ++frame) {
        // 随机生成模拟的 RGB 数据
        for (int i = 0; i < width * height * 3; ++i) {
            inputFrame[i] = dis(gen);
        }

        // 使用 SIMD 转换灰度
        to_gray_simd(inputFrame, outputFrame, width, height);

        // 打印进度
        if (frame % 30 == 0) {
            std::cout << "Processed frame: " << frame + 1 << "/" << frameCount << std::endl;
        }
    }

    auto endTime = std::chrono::high_resolution_clock::now();
    double elapsedTime = std::chrono::duration<double>(endTime - startTime).count();

    // 打印处理时间
    std::cout << "Processed " << frameCount << " frames in " << elapsedTime << " seconds." << std::endl;
    std::cout << "Average time per frame: " << (elapsedTime / frameCount) << " seconds." << std::endl;
}

结论

C++
Processed 1800 frames in 251.789 seconds.
Average time per frame: 0.139883 seconds.

C++ thread
Processed 1800 frames in 229.571 seconds.
Average time per frame: 0.12754 seconds.

CPU版本POCL的OPENCL
Processed 1800 frames in 233.25 seconds.
Average time per frame: 0.129583 seconds.

SIMD 内存对齐以后
Processed 1800 frames in 191.015 seconds.
Average time per frame: 0.106119 seconds.

SIMD的性能明显由于其他几项,但是还需要测试GPU版本的OPencl和多线程指令集优化对性能的提升

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2278891.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

RC2在线加密工具

RC2是由著名密码学家Ron Rivest设计的一种传统对称分组加密算法&#xff0c;它可作为DES算法的建议替代算法。RC2是一种分组加密算法&#xff0c;RC2的密钥长度可变&#xff0c;可以从8字节到128字节&#xff0c;安全性选择更加灵活。 开发调试上&#xff0c;有时候需要进行对…

玩转大语言模型——使用graphRAG+Ollama构建知识图谱

系列文章目录 玩转大语言模型——ollama导入huggingface下载的模型 玩转大语言模型——langchain调用ollama视觉多模态语言模型 玩转大语言模型——使用graphRAGOllama构建知识图谱 文章目录 系列文章目录前言下载和安装用下载项目的方式下载并安装用pip方式下载并安装 生成知…

【王树森搜索引擎技术】相关性01:搜索相关性的定义与分档

工业界是怎么做的&#xff1f; 制定标注规则 -> 标注数据 -> 训练模型 -> 线上推理搜索产品和搜索算法团队定义相关性标注规则 认为地将 (q,d) 相关性划分为 4个 或 5个 档位相关性文档规则非常重要&#xff01;假如日后有大幅度变动&#xff0c;需要重新标注数据&am…

学习threejs,使用FlyControls相机控制器

&#x1f468;‍⚕️ 主页&#xff1a; gis分享者 &#x1f468;‍⚕️ 感谢各位大佬 点赞&#x1f44d; 收藏⭐ 留言&#x1f4dd; 加关注✅! &#x1f468;‍⚕️ 收录于专栏&#xff1a;threejs gis工程师 文章目录 一、&#x1f340;前言1.1 ☘️THREE.FlyControls 相机控制…

LabVIEW 程序中的 R6025 错误

R6025错误 通常是 运行时库 错误&#xff0c;特别是与 C 运行时库 相关。这种错误通常会在程序运行时出现&#xff0c;尤其是在使用 C 编译的程序或依赖 C 运行时库的程序时。 ​ 可能的原因&#xff1a; 内存访问冲突&#xff1a; R6025 错误通常是由于程序在运行时访问无效内…

第4章 Kafka核心API——Kafka客户端操作

Kafka客户端操作 一. 客户端操作1. AdminClient API 一. 客户端操作 1. AdminClient API

Tarjan算法笔记

Tarjan 内容概要 dfs 搜索树 首先&#xff0c;我们要知道&#xff0c;Tarjan 算法来源于搜索树&#xff0c;那是什么呢&#xff0c;顾名思义就是按照搜索的顺序来遍历&#xff0c;所产生的顺序构成的树。首先我们可以来举个有向图的例子&#xff1a; 所以我们可以知道 dfs 生…

socket网络通信基础

目录 一、套接字编程基本流程 二、TCP流式协议及Socket编程的recv()和send() 三、读写无阻塞-完美掌握I/O复用 select&#xff08;&#xff09;函数详解 poll&#xff08;&#xff09;函数详解 epoll () 函数详解 一、套接字编程基本流程 原文链接&#xff1a;Socket编程…

接口防篡改+防重放攻击

接口防止重放攻击&#xff1a;重放攻击是指攻击者截获了一次有效请求(如交易请求),并在之后的时间里多次发送相同的请求&#xff0c;从而达到欺骗系统的目的。为了防止重放攻击&#xff0c;通常需要在系统中引入一种机制&#xff0c;使得每个请求都有一个唯一的标识符(如时间戳…

庄小焱——2024年博文总结与展望

摘要 大家好&#xff0c;我是庄小焱。岁末回首&#xff0c;2024 年是我在个人成长、博客创作以及生活平衡方面收获颇丰的一年。这一年的经历如同璀璨星辰&#xff0c;照亮了我前行的道路&#xff0c;也为未来的发展奠定了坚实基础。 1. 个人成长与突破 在 2024 年&#xff0c…

在线base64转码工具

在线base64转码工具&#xff0c;无需登录&#xff0c;无需费用&#xff0c;用完就走。 官网地址&#xff1a; https://base64.openai2025.com 效果&#xff1a;

鸿蒙学习构建视图的基本语法(二)

一、层叠布局 // 图片 本地图片和在线图片 Image(https://developer.huawei.com/allianceCmsResource/resource/HUAWEI_Developer_VUE/images/080662.png) Entry Component//自适应伸缩 设置layoutWeight属性的子元素与兄弟元素 会按照权重进行分配主轴的空间// Position s…

OA-CNN:用于 3D 语义分割的全自适应稀疏 CNN

大家读完觉得有帮助记得及时关注和点赞&#xff01;&#xff01;&#xff01; 1介绍 2相关工作 基于点的学习。 基于 CNN 的学习。 动态卷积。 3全能自适应 3D 稀疏 CNN 3.1空间适应性感受野 赋予动机。 体素网格。 金字塔网格分区。 Adaptive 聚合器。 3.2自适应关…

利用 LNMP 实现 WordPress 站点搭建

部署MySQL数据库 在主机192.168.138.139主机部署数据库服务 包安装数据库 apt-get install mysql-server 创建wordpress数据库和用户并授权 mysql> create database wordpress;#MySQL8.0要求指定插件 mysql> create user wordpress192.168.138.% identified with mys…

Vue2.0的安装

1.首先查看是否已经安装了node.js 选择以管理员方式打开命令提示符&#xff08;权限较高&#xff09;&#xff0c;或者通过cmd的方式打开 打开后输入node -v 查看自己电脑是否安装node&#xff0c;以及版本号 node -v 如果没有的话&#xff0c;请查看Node.js的安装 2.Vue和脚…

OpenEuler学习笔记(一):常见命令

OpenEuler是一个开源操作系统&#xff0c;有许多命令可以用于系统管理、软件安装、文件操作等诸多方面。以下是一些常见的命令&#xff1a; 一、系统信息查看命令 uname 用途&#xff1a;用于打印当前系统相关信息&#xff0c;如内核名称、主机名、内核版本等。示例&#xff…

无纸化同屏解决方案探究和技术展望

好多开发者&#xff0c;在了解到我们在无纸化同屏、智慧教育场景的碾压式行业积累后&#xff0c;希望我们做些无纸化同屏相关的技术探讨&#xff0c;实际上这块方案并不复杂&#xff0c;很容易做到实际使用场景契合的方案&#xff0c;主要是如何达到客户期望的功能和体验。 无…

nss刷题3

[SWPUCTF 2022 新生赛]webdog1__start level1&#xff1a; 打开环境后什么也&#xff0c;没有&#xff0c;查看源码&#xff0c;看到第一关是MD5值&#xff0c;要get传参web&#xff0c;然后web的值的MD5和它原来值相等&#xff0c;0e开头的字符在php中都是0&#xff0c;传入…

深入了解计算机网络中的路由协议与性能优化

在计算机网络中&#xff0c;路由协议是决定数据如何从源节点到达目标节点的关键组成部分。不同的路由协议各有特点&#xff0c;如何根据实际需求选择合适的协议&#xff0c;并对网络性能进行优化&#xff0c;是每个网络管理员需要面临的重要课题。 本篇文章将深入探讨计算机网…

通过视觉语言模型蒸馏进行 3D 形状零件分割

大家读完觉得有帮助记得关注和点赞&#xff01;&#xff01;&#xff01;对应英文要求比较高&#xff0c;特此说明&#xff01; Abstract This paper proposes a cross-modal distillation framework, PartDistill, which transfers 2D knowledge from vision-language models …