C ++在固定大小9的右填充空终止字符数组中找到第一个空格的最快方法

Posted

技术标签:

【中文标题】C ++在固定大小9的右填充空终止字符数组中找到第一个空格的最快方法【英文标题】:C++ Fastest way to find first space in right-padded null-terminated char array of fixed size 9 【发布时间】:2011-11-07 22:15:57 【问题描述】:

字符串的平均长度为 4 个字符。我在想二进制搜索可能是从位置 4 开始最快的。另外我认为内联模板化函数可能会表现良好。这是在一个非常紧凑的循环中完成的,因此性能至关重要。

数据如下:

"1234    "
"ABC     "
"A1235   "
"A1235kgo"

【问题讨论】:

字符是否已排序?否则,二分查找将不起作用。 我认为如果字符串的大小为 9,你不应该那么担心......无论如何,你必须检查所有内容才能找到它,所以一个简单的 for 应该可以做到...... 整个字符串中是否有特定的空格分布模式?它们总是出现在最后吗?他们总是组合在一起吗? @BenjaminLindley 不,它们不是,但是空格总是出现在末尾。 @chriskirk:好的,那么这是排序的,虽然不是传统意义上的。它按从非空格到空格的顺序排列。根据 MSN 的回答,您可以在这种情况下使用二进制搜索。但是对于这么小的数据集,二分搜索的每次检查成本可能会超过线性搜索中可能进行的少数额外检查的成本。如果您真的担心,请务必对其进行分析。 【参考方案1】:
char* found = std::find(arr, arr+9, ' ');

请注意,“不匹配”是用结束迭代器发出的信号:

bool match = (arr+9) != found;

注意,

除非您的字符处于某些已知顺序,否则二进制搜索不适用。 std::find 是内联、模板化的,如果您打开优化,它将发挥最大作用(例如,-O3 -march=native 用于 g++)

编辑 因为您展示了更多代码,我现在意识到您实际上想要检测(子)字符串长度。你可以使用

std::string::find_first_of std::string::find_last_of std::string::find std::string::rfind 等

当然,这假设您想要将 char[] 转换为 std::string 。实际上,这可能是一个完全有效的想法,因为在 C++ 标准库的几乎所有实现中都可以找到 SSO(小字符串优化)。 (参见 Herb Sutter 的 More Exceptional C++ 中的第 13-16 项,或 Scott Meyers 在 Effective STL 中对商业 std::string 实现的讨论)。

【讨论】:

我认为检查 pos 4 然后 2 或 6 然后 3 或 5 等等。会导致比 std::find() 更少的检查,因为我认为 std::find 从 pos 0 开始? @chriskirk:是的,但是如果实际上是在前几个字符中,您将不得不回溯,从而失去您认为获得的所有性能。此外,分支指令减轻了 CPU 流水线操作,因此您将无法达到标准线性 (SSE) 字符串操作的性能。 当然,如果您已经认为自己知道答案,您可以尝试并分析一下... :) @chriskirk 我认为对于如此简单的事情需要做很多额外的工作。如果早上煮一杯咖啡需要 15 秒,是否真的值得重新布置厨房以使其 13 秒? 可以使用二分查找;序列已经排序。非空格字符位于空格字符之前。 @sehe 我在哪里可以阅读有关这些标准线性 (SSE) 字符串操作的更多信息?我倾向于认为比较越少越好,但我看到你的观点。所以你同意 std::find 会是这里最快的?【参考方案2】:

您确实可以使用二分查找来查找第一个空格字符(在本例中使用std::lower_bound(...)):

const char *data= ...;// 8 character string to search

const char *end= std::lower_bound(data, data + 8, ' ', [](char lhs, char rhs)

    bool lhs_is_space= lhs==' ';
    bool rhs_is_space= rhs==' ';

    return lhs_is_space < rhs_is_space;
);

这有效地使用二分搜索来查找第一个空格字符。基本思想是假设非空格字符是false,空格字符是true,并进一步假设所有非空格字符都在空格字符之前。如果是这样,那么就按照这个分类对序列进行排序,我们可以简单地找到空格字符的开始(即下限)。

【讨论】:

@sehe 字符只有 A-Z、0-9 和空格。考虑到条件,您认为这会更快吗? 假设文本不包含控制字符(或者如果它是制表符,也被认为是尾随空格),您可以说:end = std::lower_bound(arr, arr+8, ' ', std::greater&lt;char&gt;())。我部分基于 SGI 的lower_bound 页面 (Note that you may use an ordering that is a strict weak ordering but not a total ordering) 上的脚注 [1] @chriskirk:我绝对不认为这会更快。事实上,我预计这会慢一个数量级,因为数组尺寸较小 我以前从未在 C++ 中见过这种语法。这是 C++11 吗? @默认,是的。那是一个 lambda。【参考方案3】:

由于空格都在末尾,您可以使用展开的二进制搜索。但是,常规线性搜索的速度奇迹般地接近,并且不会让未来的开发人员讨厌你。

inline int find_space(char (&data)[9]) 
    if (data[3] == ' ') 
        if (data[1] == ' ') 
            if (data[0] == ' ')
                return 0;
            return 1;
         else if (data[2] == ' ')
            return 2;
        return 3; 
    
    if (data[5] == ' ') 
        if (data[4] == ' ')
            return 4;
        return 5;
     else if (data[7] == ' ') 
        if (data[6] == ' ')
            return 6;
        return 7;
     else if (data[8] == ' ')
        return 8;
    return -1;

【讨论】:

我会给你 +1 的努力。不过很容易看出,运行时间将被分支指令支配,除非一个非常聪明的编译器将它全部优化为......相当于线性字符扫描:) 另外,你证明了正确性吗?哈哈。你让我想测试一下。真可惜:) @sehe:我没有验证我的代码,如果某处没有错误,我会有点惊讶。即使在线性字符扫描中,你也会有if char == ' ' then return,所以分支仍然存在。虽然线性可能更好。【参考方案4】:

让编译器和优化器完成它的工作。

inline
template <typename T_CHAR, int N>
T_CHAR* find_first_of(T_CHAR a[N], T_CHAR t)

    for (int ii = 0; ii < N; ++ii)
    
        if (a[ii] == t)  return a+ii; 
    
    return NULL;

或者让标准模板库的作者为您完成所有繁重的工作。

inline
template <typename T_CHAR, int N>
T_CHAR* find_first_of(T_CHAR a[N], T_CHAR t)

    T_CHAR* ii = std::find(a, a+N, t);
    if (ii == a+N) return NULL;
    return ii;

【讨论】:

【参考方案5】:

只有我的 2 美分。我想所有字符串的长度都是8。可能的字符'A'-'Z', 'a'-'z'、'0'-'9' 和空格。 我试过了:

//simple
const char *found = std::find(x.data, x.data + 9, ' ');
//binary search
const char *end = std::lower_bound(x.data, x.data + 8, ' ', [](char lhs, char rhs) 

和我的优化版本(它取决于编译器==gcc)(见下文)。 我在 Linux 64 位上进行了测试,使用 -O3 -march=native -std=c++0x。 随机生成 50000000 个字符串的结果:

简单取0.480000, 优化取 0.120000, 二分查找需要 0.600000。

union FixedLenStr 
     unsigned char chars[8];
     uint32_t words[2];
     uint64_t  big_word;
;

static int space_finder(const char *str) 
   
        FixedLenStr tmp;

        memcpy(tmp.chars, str, 8);

        tmp.big_word &= 0xF0F0F0F0F0F0F0F0ull;
        tmp.big_word >>= 4;
        tmp.big_word = (0x0707070707070707ull - tmp.big_word) * 26;
        tmp.big_word &= 0x8080808080808080ull;      

        return (__builtin_ffsll(tmp.big_word) >> 3) - 1;    

【讨论】:

以上是关于C ++在固定大小9的右填充空终止字符数组中找到第一个空格的最快方法的主要内容,如果未能解决你的问题,请参考以下文章

C和指针--编程题9.14第10小题--判断回文函数

C语言中怎么把一维数组初始化都为0,

用C语言实现一维数组中删除第N个元素的程序怎么写

C的string.h里有没有字符串替换函数

C程中如何计算数组(一维及二维)占内存空间的大小

C ++中动态大小的二维数组中的用户输入