gpt4 book ai didi

c++ - 为什么在循环之前将 const 标量值分配给 const 会有所帮助?

转载 作者:塔克拉玛干 更新时间:2023-11-03 00:28:17 25 4
gpt4 key购买 nike

在 GCC 5.4.0 的 STL_algobase.h 中,我们有:

  template<typename _ForwardIterator, typename _Tp>
inline typename
__gnu_cxx::__enable_if<!__is_scalar<_Tp>::__value, void>::__type
__fill_a(_ForwardIterator __first, _ForwardIterator __last,
const _Tp& __value)
{
for (; __first != __last; ++__first)
*__first = __value;
}

template<typename _ForwardIterator, typename _Tp>
inline typename
__gnu_cxx::__enable_if<__is_scalar<_Tp>::__value, void>::__type
__fill_a(_ForwardIterator __first, _ForwardIterator __last,
const _Tp& __value)
{
const _Tp __tmp = __value;
for (; __first != __last; ++__first)
*__first = __tmp;
}

我不明白为什么标量变体比一般变体有任何优势。我的意思是,它们不会被编译成完全相同的东西吗?将 __value 从堆栈加载到寄存器并在整个循环中使用该寄存器?

最佳答案

这起源于 2004 年的 SVN 修订版 83645(git commit 8ba26e53),当时两个 __fill_a 变体都作为辅助结构实现:

template<typename>
struct __fill
{
template<typename _ForwardIterator, typename _Tp>
static void
fill(_ForwardIterator __first, _ForwardIterator __last,
const _Tp& __value)
{
for (; __first != __last; ++__first)
*__first = __value;
}
};

template<>
struct __fill<__true_type>
{
template<typename _ForwardIterator, typename _Tp>
static void
fill(_ForwardIterator __first, _ForwardIterator __last,
const _Tp& __value)
{
const _Tp __tmp = __value;
for (; __first != __last; ++__first)
*__first = __tmp;
}
};

关于此主题的文档很少,但 Dan Nicolaescu 和 Paolo Carlini 的原始提交在提交消息中包含一个提示:

  • include/bits/stl_algobase.h (__fill, __fill_n): New helpers for fill and fill_n, respectively: when copying is cheap, use a temporary to avoid a memory read in each iteration.

考虑到那些是/曾经是标准库的维护者,我认为他们知道他们在做什么:他们解决了引用通常作为指针实现的问题。毕竟,它们只是一个已经存在的内存位置的新别名。这就是最初有两种变体的原因。请注意,__true_type 是在 fill 调用中决定的:

  typedef typename __type_traits<_Tp>::has_trivial_copy_constructor
_Trivial;
std::__fill<_Trivial>::fill(__first, __last, __value);

使用 std::enable_if,或者更确切地说是它的 GCC 变体,Carlini 删除了那些助手,并用您已经提供的版本替换了它们。逻辑仍然成立:对于标量类型,您希望具有一些本地值。如果您的范围位于另一个内存区域而不是您的值并且跨越多个页面并溢出您的 L1 缓存,您不希望为该值锁定缓存的一部分。这对于局部变量来说是微不足道的。

然而,语义很重要。 std::fill 生成精确的 std::distance(first, last) 拷贝。使用标量值,我们知道额外的拷贝不会有副作用。使用用户定义的类型?好吧,我们不知道。这就是为什么您不能在第一种情况下使用 const auto tmp = __value; 变体。

这就是为什么您最终得到两个,实际上是三个变体的原因:

  • 一个用于标量值,您可以保持该值“接近”并帮助优化器
  • 一个用于类似字节的值,您可以在其中使用 memset
  • 一个用于所有其他类型,在这些类型中您不能干涉语义。

关于c++ - 为什么在循环之前将 const 标量值分配给 const 会有所帮助?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/40057431/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com