【发布时间】:2013-05-09 21:22:56
【问题描述】:
我分析了我的一个程序,发现热点是levenshtein_distance,递归调用。我决定尝试优化它。
lvh_distance levenshtein_distance( const std::string & s1, const std::string & s2 )
{
const size_t len1 = s1.size(), len2 = s2.size();
std::vector<unsigned int> col( len2+1 ), prevCol( len2+1 );
const size_t prevColSize = prevCol.size();
for( unsigned int i = 0; i < prevColSize; i++ )
prevCol[i] = i;
for( unsigned int i = 0, j; i < len1; ++i )
{
col[0] = i+1;
const char s1i = s1[i];
for( j = 0; j < len2; ++j )
{
const auto minPrev = 1 + std::min( col[j], prevCol[1 + j] );
col[j+1] = std::min( minPrev, prevCol[j] + ( static_cast<unsigned int>( s1i != s2[j] ) ) );
}
col.swap( prevCol );
}
return prevCol[len2];
}
TL;DR:我改变了std::string → std::array
战争故事:
在它上面运行 vtune 之后,我发现更新 col[j+1] 的那一行会减慢一切(90% 的时间都花在它上面)。我想:好吧,也许这是一个别名问题,也许编译器无法确定字符串对象中的字符数组是无别名的,因为它们被字符串接口屏蔽,并且花费 90% 的时间检查程序的其他部分修改它们。
所以我把我的字符串改成了一个静态数组,因为那里没有动态内存,下一步就是使用restrict来帮助编译器。但与此同时,我决定检查一下我这样做是否获得了任何性能。
lvh_distance levenshtein_distance( const std::string & s1, const std::string & s2 )
{
const size_t len1 = s1.size(), len2 = s2.size();
static constexpr unsigned MAX_STRING_SIZE = 512;
assert(len1 < MAX_STRING_SIZE && len2 < MAX_STRING_SIZE);
static std::array<unsigned int, MAX_STRING_SIZE> col, prevCol;
for( unsigned int i = 0; i < len2+1; ++i )
prevCol[i] = i;
// the rest is unchanged
}
TL;DR:现在它运行缓慢。
发生的事情是我失去了表现。很多。我的示例程序现在在 44 秒内运行,而不是在 ~ 6 秒内运行。再次使用 vtune 进行分析表明,一个函数被一遍又一遍地调用:std::swap(对你来说,gcc 人,这是在 bits/move.h 中),它又被 std::swap_ranges(bits/stl_algobase.h)调用。 h)。
我认为std::min 是使用quicksort 实现的,这就解释了为什么要进行交换,但我不明白为什么在这种情况下交换需要这么多时间。
编辑:编译器选项:我正在使用带有选项“-O2 -g -DNDEBUG”和一堆警告说明符的 gcc。
【问题讨论】:
-
我猜如果你交换
vectors,你交换指向底层数组的指针,如果你交换arrays,数组是使用临时复制的。 -
min永远不应该通过排序来实现,因为通过顺序扫描查找值比任何可能的排序算法都快! -
@dionadar 你的意思是
std::minimal 例子(好吧,我会停下来;)) -
在优化的构建中,线条很容易混淆,你确定这不是真正的瓶颈吗:
col.swap( prevCol );。您应该尝试让std::array<...> *pCol, *pPrevCol;指向两个真实数组,通过指针更改所有访问并交换指针......或者一次执行外部循环两次迭代并手动交换col和prevCol后半部分(你需要一个额外的测试并在两半之间休息,return语句需要某种条件)。 -
@CharlesBailey 使用 Howard Hinnant 的 stack allocator for
std::vector结合了两全其美:指针交换和无动态分配。
标签: c++ arrays optimization vector