跳到主要内容

高效查找数组中的特定值

在 C++ 编程中,我们经常需要在 std::vector<char>char[] 中查找特定值(例如 1),以确定数据的有效部分。在这种情况下,可以使用 for 循环、std::find_ifstd::memchr,甚至 SIMD 指令集(如 AVX2)来优化查找过程。

本文将详细分析 不同方法的理论性能、适用场景、代码示例,并提供 实际测试数据 供参考。

1. 介绍 std::memchr

std::memchr 是什么?

std::memchr 是 C 语言标准库中的一个函数,专门用于在 连续的内存块(如 char[]std::vector<char>)中查找 第一个匹配的字节

函数定义

void* memchr(const void* ptr, int value, size_t num);

参数:

  • ptr:指向要搜索的内存块的指针(通常是 char*void*)。
  • value:要查找的字节值(会转换为 unsigned char)。
  • num:要搜索的 字节数size_t)。

返回值:

  • 如果找到 value,返回指向该字节的指针。
  • 如果未找到,返回 nullptr

适用场景

  • std::memchr 通常比 for 循环快,因为它的底层实现可能利用了 SIMD(Single Instruction Multiple Data)加速
  • 适用于 二进制数据 处理,例如 char[]std::vector<char>

2. 在 std::vector<char> 中使用 std::memchr

示例:在 std::vector<char> 中查找 1

#include <iostream>
#include <vector>
#include <cstring> // std::memchr

int main() {
std::vector<char> data(10, 0); // 初始化 10 个 0
data[3] = 1; // 设置索引 3 和 7 为 1
data[7] = 1;

void* result = std::memchr(data.data(), 1, data.size());

if (result) {
int index = static_cast<char*>(result) - data.data();
std::cout << "Found first 1 at index: " << index << std::endl;
} else {
std::cout << "Not found!" << std::endl;
}
return 0;
}

3. std::memchr vs std::find_if vs for vs SIMD

代码实现

方法 1:传统 for 循环

int for_loop_method(const std::vector<char>& data, int start, int end) {
for (int i = start; i <= end; ++i) {
if (data[i] != 0) return i;
}
return -1;
}

方法 2:std::find_if

#include <algorithm>
int find_if_method(const std::vector<char>& data, int start, int end) {
auto it = std::find_if(data.begin() + start, data.begin() + end + 1, [](char c) { return c != 0; });
return (it != data.begin() + end + 1) ? std::distance(data.begin(), it) : -1;
}

方法 3:std::memchr

int memchr_method(const std::vector<char>& data, int start, int end) {
void* result = std::memchr(data.data() + start, 1, end - start + 1);
return result ? static_cast<char*>(result) - data.data() : -1;
}

方法 4:SIMD (AVX2)

#include <immintrin.h>
int simd_method(const std::vector<char>& data, int start, int end) {
constexpr int SIMD_WIDTH = 32;
int aligned_start = start;
while (aligned_start + SIMD_WIDTH <= end) {
__m256i chunk = _mm256_loadu_si256(reinterpret_cast<const __m256i*>(data.data() + aligned_start));
int mask = _mm256_movemask_epi8(_mm256_cmpeq_epi8(chunk, _mm256_setzero_si256()));
if (mask != 0xFFFFFFFF) {
for (int i = 0; i < SIMD_WIDTH; ++i) {
if (data[aligned_start + i] != 0) {
return aligned_start + i;
}
}
}
aligned_start += SIMD_WIDTH;
}
for (int i = aligned_start; i <= end; ++i) {
if (data[i] != 0) {
return i;
}
}
return -1;
}

4. 性能测试

测试方法

  • 生成 std::vector<char>,大小 600
  • 随机插入 1 的位置,保证不同方法使用相同数据。
  • 随机选择 startend,测试 100,000 次。
  • 使用 std::chrono 记录时间。

image-20250313110119943

image-20250313110139614

image-20250313110153543

测试结果

方法执行时间(秒)
for 循环2.491s
std::find_if1.585s
std::memchr0.719s
SIMD (AVX2)0.999s

5. 结论

  • for 循环 最慢,不推荐用于大数据。
  • std::find_if 适用于 STL 容器,但比 memchr 略慢。
  • std::memchr 速度最快
  • SIMD (AVX2) 次快,适用于大数据优化。

推荐:

  • 小数据量std::find_if,可读性好。
  • 中等数据量std::memchr,速度更快。
  • 大数据量SIMD (AVX2),极致优化。

理论上std::memchr与SIMD数据应该类似,这里可能是规模较小没有体现SIMD的优势