2

C++ map自定义比较函数遵守严格弱序 - 真昼小天使daisuki

 7 months ago
source link: https://www.cnblogs.com/kazusarua/p/18015058
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
neoserver,ios ssh client

C++ map自定义比较函数遵守严格弱序

问题背景及定位

背景:这个问题是在将tablesaw(一个Java的数据处理项目)迁移到C++时出现的。

问题位置:SplitOn()函数,在数据流水线中的aggregate阶段。

问题描述:使用google/benchmark进行了批量化的性能测试,在测试中出现偶发性段错误,几率大约在万分之一到十万分之一之间。

问题定位:由于开发环境为受限环境,无法使用GDB调试查看堆栈定位,只能使用打印日志的方式处理

定位问题出现在如下代码处:

struct ByteArrayCompare {
    bool operator()(const ByteArray &a, const ByteArray &b) const {
        for (int i = 0; i < min(a.byteArray.size(), b.byteArray.size()); i++)
		{
			if (a.byteArray[i] != b.byteArray[i])
				return a.byteArray[i] < b.byteArray[i];
		}
		return true;
    }
    typedef ByteArray value_type;
};

......

map<ByteArray, Selection, ByteArrayCompare> selectionMap;

......

selectionMap[instanceByteArray] = std::move(selection); # crash here

至此,我个人百思不得其解,按照常理来说,应该是没有问题的。在没有段错误的情况下,测试用例能够顺利通过。

刚开始以为是class Selection的右值引用问题,有内存分配/释放没有构造/析构好,或者是移动构造出现问题,经过思考和检查排除以上问题。

因此定位问题出现在map自定义的ByteArrayCompare函数上。

map定义参见文档:https://cplusplus.com/reference/map/map/

template < class Key,                                   //map::key_tpe
           class T,                                     //map::mapped_type
           class Compare = less<Key>,                   //map::key_compare
           class Alloc = allocator<pair<const Key, T>>  //map::allocator_type
           > class map;

由以上代码可见,map是可以自定义Compare比较函数和Alloc分配器的,此处就使用了自定义的Compare比较函数,应用于ByteArray数据类型。

题外话:unordered_map可以自定义hash和equal函数,这也体现了STL对于两种数据结构的不同实现方式,此处不再展开。

问题原因及解决方案

这里我们需要一个概念strict_weak_order(严格弱序)

本篇文章在数学和语义上阐述了严格弱序的意义,值得一看:https://zhuanlan.zhihu.com/p/378294506

抛开复杂的逻辑不谈,简单来说,该性质要求比较函数对于两个不同的key,改变输入顺序不会改变比较结果。

例:(a, b)形式输入,输出结果为a < b(假设为false),(b, a)形式输入,输出结果应该为true,若为仍false则会出现问题。

具体到我们此处的代码:此时我们已经遍历完成了a和b中较短的那个,但是对于剩余长度,没有进行比较,而是直接返回true,因此出现了上述的非严格弱序问题。

修改后代码:

struct ByteArrayCompare {
    bool operator()(const ByteArray &a, const ByteArray &b) const {
        for (int i = 0; i < min(a.byteArray.size(), b.byteArray.size()); i++)
		{
			if (a.byteArray[i] != b.byteArray[i])
				return a.byteArray[i] < b.byteArray[i];
		}
		return a.byteArray.size() < b.byteArray.size();
    }
    typedef ByteArray value_type;
};

......

map<ByteArray, Selection, ByteArrayCompare> selectionMap;

......

selectionMap[instanceByteArray] = std::move(selection); # crash here

至此,再进行测试后不会出现上述段错误问题,问题解决。


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK