以前一直在使用ipp库,但是ipp库仅针对Intel的处理器,最近需要将服务器从Intel的CPU替换到华为鲲鹏,IPP的加速库便不不能再使用了。所以在考虑使用鲲鹏的KML库替换还是直接用标准库替换,萌生了对比IPP可和标准库效率的想法。对比代码如下:

#include <QCoreApplication>
#include <QElapsedTimer>
#include <iostream>
#include "./Include/ipp/ipp.h"
#include <cstring> // 标准库函数
#include <random>
#include <algorithm>
#include <iterator>

// Windows环境需要显式链接IPP库
#pragma comment(lib, "ippsmt.lib")
#pragma comment(lib, "ippcoremt.lib")

constexpr int SIZE = 1 << 20;   // 1MB数据(262,144个float)
constexpr int TRIALS = 1000;    // 测试次数

// 测试IPP函数
void testIPP() {
    Ipp32f* src = nullptr, * dst = nullptr;
    QElapsedTimer timer;

    // 内存分配
    timer.start();
    for (int i = 0; i < TRIALS; ++i) {
        src = ippsMalloc_32f(SIZE);
        ippsFree(src);
    }
    auto mallocTime = timer.nsecsElapsed() / TRIALS;

    // 内存赋值
    src = ippsMalloc_32f(SIZE);
    timer.start();
    for (int i = 0; i < TRIALS; ++i) {
        ippsSet_32f(3.14159f, src, SIZE);
    }
    auto setTime = timer.nsecsElapsed() / TRIALS;
    ippsFree(src);

    // 内存置零
    src = ippsMalloc_32f(SIZE);
    timer.start();
    for (int i = 0; i < TRIALS; ++i) {
        ippsZero_32f(src, SIZE);
    }
    auto zeroTime = timer.nsecsElapsed() / TRIALS;
    ippsFree(src);

    // 内存拷贝
    src = ippsMalloc_32f(SIZE);
    dst = ippsMalloc_32f(SIZE);
    ippsSet_32f(2.71828f, src, SIZE);
    timer.start();
    for (int i = 0; i < TRIALS; ++i) {
        ippsCopy_32f(src, dst, SIZE);
    }
    auto copyTime = timer.nsecsElapsed() / TRIALS;
    ippsFree(src);
    ippsFree(dst);

    std::cout << "[IPP] Malloc: " << mallocTime << " ns\n"
        << "[IPP] Set:    " << setTime << " ns\n"
        << "[IPP] Zero:   " << zeroTime << " ns\n"
        << "[IPP] Copy:   " << copyTime << " ns\n";
}

// 测试标准库
void testSTD() {
    float* src = nullptr, * dst = nullptr;
    QElapsedTimer timer;

    // 内存分配
    timer.start();
    for (int i = 0; i < TRIALS; ++i) {
        src = new float[SIZE];
        delete[] src;
    }
    auto mallocTime = timer.nsecsElapsed() / TRIALS;

    // 内存赋值(循环)
    src = new float[SIZE];
    timer.start();
    for (int i = 0; i < TRIALS; ++i) {
#pragma omp simd for
        for (int i = 0; i < SIZE; ++i)
            src[i] = 3.14159f;
        //std::fill_n(src, SIZE, 3.14159f);
    }
    auto setTime = timer.nsecsElapsed() / TRIALS;
    delete[] src;

    // 内存置零(memset)
    src = new float[SIZE];
    timer.start();
    for (int i = 0; i < TRIALS; ++i) {
        memset(src, 0, SIZE * sizeof(float));
    }
    auto zeroTime = timer.nsecsElapsed() / TRIALS;
    delete[] src;

    // 内存拷贝(memcpy)
    src = new float[SIZE];
    dst = new float[SIZE];
    memset(src, 0, SIZE * sizeof(float));
    timer.start();
    for (int i = 0; i < TRIALS; ++i) {
        memcpy(dst, src, SIZE * sizeof(float));
    }
    auto copyTime = timer.nsecsElapsed() / TRIALS;
    delete[] src;
    delete[] dst;

    std::cout << "[STD] Malloc: " << mallocTime << " ns\n"
        << "[STD] Set:    " << setTime << " ns\n"
        << "[STD] Zero:   " << zeroTime << " ns\n"
        << "[STD] Copy:   " << copyTime << " ns\n";
}

// 生成随机浮点数组
void generateRandomData(float* data, size_t size) {
    std::random_device rd;
    std::mt19937 gen(rd());
    std::uniform_real_distribution<float> dist(0.0f, 1000.0f);
    for (size_t i = 0; i < size; ++i) {
        data[i] = dist(gen);
    }
}

// 标准库计算均值与标准差
void stdMeanStdDev(const float* data, size_t size, float& mean, float& stdDev) {
    double sum = 0.0, sumSq = 0.0;
    for (size_t i = 0; i < size; ++i) {
        sum += data[i];
        sumSq += data[i] * data[i];
    }
    mean = sum / size;
    stdDev = std::sqrt((sumSq - sum * sum / size) / (size - 1)); // 样本标准差
}

// 性能测试主函数
void performanceTest(size_t dataSize, int iterations) {
    // 内存分配与数据初始化
    Ipp32f* ippData = ippsMalloc_32f(dataSize);
    std::vector<float> stdData(dataSize);
    generateRandomData(ippData, dataSize);
    std::copy(ippData, ippData + dataSize, stdData.begin());

    // 预热缓存(可选)
    std::sort(stdData.begin(), stdData.end());

    // 测试排序性能
    auto start = std::chrono::high_resolution_clock::now();
    for (int i = 0; i < iterations; ++i) {
        ippsSortAscend_32f_I(ippData, dataSize); // IPP原地排序
    }
    auto ippSortTime = std::chrono::duration_cast<std::chrono::milliseconds>(
        std::chrono::high_resolution_clock::now() - start
    ).count();

    start = std::chrono::high_resolution_clock::now();
    for (int i = 0; i < iterations; ++i) {
        std::sort(stdData.begin(), stdData.end()); // 标准库排序
    }
    auto stdSortTime = std::chrono::duration_cast<std::chrono::milliseconds>(
        std::chrono::high_resolution_clock::now() - start
    ).count();

    // 测试均值与标准差计算性能
    float ippMean, ippStdDev;
    start = std::chrono::high_resolution_clock::now();
    for (int i = 0; i < iterations; ++i) {
        ippsMeanStdDev_32f(ippData, dataSize, &ippMean, &ippStdDev, ippAlgHintFast); // IPP计算
    }
    auto ippStatTime = std::chrono::duration_cast<std::chrono::milliseconds>(
        std::chrono::high_resolution_clock::now() - start
    ).count();

    float stdMean, stdStdDev;
    start = std::chrono::high_resolution_clock::now();
    for (int i = 0; i < iterations; ++i) {
        stdMeanStdDev(stdData.data(), dataSize, stdMean, stdStdDev); // 标准库计算
    }
    auto stdStatTime = std::chrono::duration_cast<std::chrono::milliseconds>(
        std::chrono::high_resolution_clock::now() - start
    ).count();

    // 输出结果
    std::cout << "===== 性能对比 (数据量: " << dataSize << ", 迭代次数: " << iterations << ") =====\n";
    std::cout << "排序:\n"
        << "  IPP ippsSortAscend_32f_I: " << ippSortTime << " ms\n"
        << "  std::sort:                " << stdSortTime << " ms\n";
    std::cout << "统计计算:\n"
        << "  IPP ippsMeanStdDev_32f:    " << ippStatTime << " ms\n"
        << "  标准库实现:                " << stdStatTime << " ms\n";

    ippsFree(ippData);
}

int main(int argc, char* argv[]) 
{
    QCoreApplication a(argc, argv);

    std::cout << "=== Intel IPP Performance ===\n";
    testIPP();

    std::cout << "\n=== Standard Library Performance ===\n";
    testSTD();

    performanceTest(1e6, 10);  // 测试100万数据,迭代10次
    return 0;
}

使用1000次对1MB数据(262,144个float)的效率对比。对比结果如下:

Windows系统Debug模式

=== Intel IPP Performance ===
[IPP] Malloc: 1293522 ns
[IPP] Set:    174418 ns
[IPP] Zero:   94937 ns
[IPP] Copy:   501033 ns

=== Standard Library Performance ===
[STD] Malloc: 1202868 ns
[STD] Set:    1940080 ns
[STD] Zero:   93922 ns
[STD] Copy:   190766 ns
===== 性能对比 (数据量: 1000000, 迭代次数: 10) =====
排序:
  IPP ippsSortAscend_32f_I: 240 ms
  std::sort:                1404 ms
统计计算:
  IPP ippsMeanStdDev_32f:    2 ms
  标准库实现:                26 ms

F:\Workspace\Code\Qt\TestIntelIpps\x64\Debug\TestIntelIpps.exe (进程 21008)已退出,代码为 0 (0x0)。
要在调试停止时自动关闭控制台,请启用“工具”->“选项”->“调试”->“调试停止时自动关闭控制台”。
按任意键关闭此窗口. . .

Windows系统Release模式

=== Intel IPP Performance ===
[IPP] Malloc: 48886 ns
[IPP] Set:    301834 ns
[IPP] Zero:   87169 ns
[IPP] Copy:   544983 ns

=== Standard Library Performance ===
[STD] Malloc: 44820 ns
[STD] Set:    710 ns
[STD] Zero:   85777 ns
[STD] Copy:   211307 ns
===== 性能对比 (数据量: 1000000, 迭代次数: 10) =====
排序:
  IPP ippsSortAscend_32f_I: 229 ms
  std::sort:                109 ms
统计计算:
  IPP ippsMeanStdDev_32f:    3 ms
  标准库实现:                0 ms

F:\Workspace\Code\Qt\TestIntelIpps\x64\Release\TestIntelIpps.exe (进程 26764)已退出,代码为 0 (0x0)。
要在调试停止时自动关闭控制台,请启用“工具”->“选项”->“调试”->“调试停止时自动关闭控制台”。
按任意键关闭此窗口. . .

Linux系统(Centos 7.9) Debug模式

=== Intel IPP Performance ===
[IPP] Malloc: 350 ns
[IPP] Set:    206298 ns
[IPP] Zero:   104632 ns
[IPP] Copy:   766895 ns

=== Standard Library Performance ===
[STD] Malloc: 145 ns
[STD] Set:    2213560 ns
[STD] Zero:   143366 ns
[STD] Copy:   837936 ns
===== 性能对比 (数据量: 1000000, 迭代次数: 10) =====
排序:
  IPP ippsSortAscend_32f_I: 300 ms
  std::sort:                1437 ms
统计计算:
  IPP ippsMeanStdDev_32f:    3 ms
  标准库实现:                31 ms
按 <RETURN> 来关闭窗口...
  

Linux系统(Centos 7.9) Release模式


=== Intel IPP Performance ===
[IPP] Malloc: 214 ns
[IPP] Set:    230157 ns
[IPP] Zero:   88688 ns
[IPP] Copy:   509544 ns

=== Standard Library Performance ===
[STD] Malloc: 42 ns
[STD] Set:    725504 ns
[STD] Zero:   274262 ns
[STD] Copy:   614060 ns
===== 性能对比 (数据量: 1000000, 迭代次数: 10) =====
排序:
  IPP ippsSortAscend_32f_I: 231 ms
  std::sort:                109 ms
统计计算:
  IPP ippsMeanStdDev_32f:    3 ms
  标准库实现:                12 ms
按 <RETURN> 来关闭窗口...

本机系统为Windows 11 专业版 24H2 CPU类型为Intel(R) Core(TM) i7-7700 CPU @ 3.60GHz   3.60 GHz。

综合对比下来,除Set外、其余函数标准库优化相当不错。接近与IPP库的性能。而SET函数IPP库有10倍以上的速度提升。

Logo

鲲鹏昇腾开发者社区是面向全社会开放的“联接全球计算开发者,聚合华为+生态”的社区,内容涵盖鲲鹏、昇腾资源,帮助开发者快速获取所需的知识、经验、软件、工具、算力,支撑开发者易学、好用、成功,成为核心开发者。

更多推荐