【发布时间】:2020-11-25 03:36:09
【问题描述】:
我为我的 uint128 结构编写了一个十六进制解析函数,它在内部只有两个 uint64_t - hi 和 lo。这是有问题的函数:
uint128_t parse_from_hex(const char *string, uint128_t previous_value, const size_t num_digits) {
if (string == NULL || string[0] == '\0')
return previous_value;
if (num_digits + 1 > 2 * SIZEOF_INT128) { // SIZEOF_INT128 is 16 - meaning 16 bytes
return UINT128_MAX; // the maximum value of 128bit uint, if we overflow
}
int64_t current_digit = parse_hex_digit(string[0]);
if (current_digit < 0)
return UINT128_ZERO; // a global variable which I use multiple times which represents a 0
return parse_from_hex(string + 1,
uint128_or_uint64(uint128_shift_left(previous_value, 4), (uint64_t)current_digit),
num_digits + 1);
}
出于某种原因,gcc 不会优化函数,即使递归调用显然是在函数末尾进行了一次。解析函数中使用的其他函数没有任何副作用并返回一个新值,所以我认为问题不在于它们。我尝试使 uint128_t 结构成员非 const(最初它们是非 const)以及函数参数非 const,但这也无济于事。最初使用 Ofast 编译,但也尝试使用 O3 和 O2 - 没有运气。有谁对这个问题有更了解的人可以帮忙吗?我以为我理解得很好,但显然我遗漏了一些东西。
【问题讨论】:
-
你有什么理由递归地这样做吗?这是一个循环的工作。
-
这是一个显示原始代码不是尾递归的上帝螺栓链接:godbolt.org/z/Ws4YTz
-
GCC 10.2 将在这里进行尾递归,Clang 不会。
-
什么版本的编译器没有做尾调用优化?
-
GCC 确实比处理更像是一种函数式编程模式的尾递归更容易优化和在许多情况下展开循环。优化器处理人们最常使用的模式。即使优化了这段代码,它看起来也比基于循环的版本复杂得多。
标签: c gcc tail-recursion