【问题标题】:swapping values of fundamental type交换基本类型的值
【发布时间】:2021-11-16 19:28:23
【问题描述】:

我想我们都知道如何交换值,但还有另一种方法:

constexpr auto assign(auto& ...a) noexcept
{
  return [&](auto const ...v) noexcept { ((a = v), ...); };
}

要交换,我们需要调用assign(a, b)(b, a)

这是交换基本类型值的有效方法吗?它是否提供了比通常方式更多的优化空间?

【问题讨论】:

  • 我以前从未见过这个“成语”,我不喜欢它:( 谷歌搜索“assign idiom”只能找到这个问题,所以看起来像是你编的。@ 987654321@
  • 这很难阅读,并且似乎复制了所有值两次(标准交换复制一个值两次,另一个值(“其余”)一次)。好处应该在哪里?
  • @JohnBayko:可能。那也可以be done with std::tie
  • 我的测试产生了相同的程序集。如果您在基准测试中有一个有趣的结果,请与我们分享,正如@HolyBlackCat 建议的那样。否则,我想我的答案是我们所能提供的。
  • "trustimplicity without ... asm" Here's the asm,(几乎)所有三种方法都相同。

标签: c++ swap


【解决方案1】:

这不是交换值的好方法,即使对于基本类型也是如此。没有标准的“分配习语”;这纯粹是你编造的。

即使它有效,也没有理由期望它会产生比std::swap 更快的代码,std::swap一个标准的、众所周知的习语。

我强烈建议您改用std::swap,这样阅读您的代码的人就可以理解,而无需使用 Google。

【讨论】:

  • 背诵经典不会使我的“成语”无效。你没有提供汇编,没有引用标准,没有任何见解。
  • @user1095108 您的问题是:“这是交换的好方法吗”,答案是否定的。您现在已将其更改为:“这是一种有效的交换方式吗?” - 答案是:它充其量与std::swap 一样好。如果您打算让其他人使用它,请不要混淆您的代码。最后,在 cmets 中已经为您提供了 asm。
  • 我没有更改/删除任何东西,这是模组,我实际上恢复了他们所做的一些更改。无论如何,据我所知,asm 未能显示 std::swap() 和“成语”编译成的任何区别。所以这是一次失败的尝试解决这个问题。
  • @user1095108 我不知道你是如何得出这个结论的。没有人试图“击落这个问题”,我们正在尝试并且已经成功地回答了它所说的。版主试图将您所有的 cmets(您已删除)链接在一起,以澄清您的问题。即便如此,考虑到您的更改,问题已经得到解答。在这一点上,我认为您的 cmets 只是垃圾邮件。
【解决方案2】:

由于您一直在要求 cmets 中的理论:编译器优化是基于编译器是否可以看到通用模式以根据优化指标将它们转换为更好的代码。

为此,编译器可以非常聪明找出以下是交换:

// Conventional (simplified) swap definition
auto swap(int& a, int& b) -> void {
    const int tmp = a;
    a = b;
    b = tmp;
}

在您提供的代码中,它应该几乎等效 仅适用于基本类型,因为它将参数const auto...v 视为tmp 对象--但是它将看到副本的两组参数。如果我们将assign(a,b)(b,a) 的变换展平,编译器从模板扩展中真正看到的是:

const auto v1 = a;
const auto v2 = b;
b = v1;
a = v2;

它与标准 swap 类似,但与经过数十年优化训练的编译器识别的内容并不完全相同。 最有可能,编译器会将其视为等效转换,并生成相同的程序集——gcc and clang 都是这种情况(感谢 @HolyBlackCat 提供 Godbolt 链接).

请注意,编译器在优化以常规/预期方式编写的代码方面非常聪明。他们倾向于不喜欢和挣扎的是试图变得聪明。特别是,assign(a,b)(b,a) 要求编译器首先对输入进行展平以进行优化——而传统的swap(a,b) 则针对它进行了详细说明。基本上:在最佳,你会得到与以传统方式做事相同的事情。

为此,这不是交换值的好方法。这可能提供更好的优化(如果有的话,可能会稍微更糟)。

如果我们扩展这个定义以包含泛型,它会变得更糟,因为const auto 提供了更多的副本,并且不执行正确的移动(并且正确的移动语义也有助于编译器)。此外,它在语义上不会读作“swap”,而swap(a,b) 甚至std::tie(a,b) = std::make_tuple(b,a) 则不那么含糊。


这也不是一个“成语”,因为它从来没有被使用确立为一种模式。

【讨论】:

  • 做非常规的事情是一条艰难的道路。 “系统”,计算机和人类一样,被优化为传统的。如果您可以完成大量作业,基准测试等,您会发现解决此类问题会更​​容易...,因为如果您不能证明自己很快就完成了某些事情,那么您会得到调出。
  • @user4581301 艰难的道路有时是唯一的道路。
【解决方案3】:

我认为如果您一次分配多个值是有意义的,其中一些受让人(不确定这是术语吗?)可能会在此过程中更改。

例如,做一个旋转:

int a = 10, b = 20, c = 30;
a = b;
b = c;
c = a; // oops you might wanted c = 10, but c is actually 20 now.

但是,如果您只交换 2 个值,那么在可读性和可优化性方面,std::swap 可能是更好的选择。

虽然可读性是主观的,至少对我而言,swap(a, b) 更容易理解,而assign(a, b)(b, a) 并不意味着直接交换的想法。

需要对可优化性进行基准测试才能确定,但​​据我所知,std::swap 已针对基本类型进行了很好的优化,我认为您的方式不会进一步优化它。但是,如果没有实际进行基准测试,我无法给您一个确定的答案。

【讨论】:

  • 我并不真正关心 std::swap 或可读性或样式,我的问题与这些无关。
  • @user1095108 你问这是否是交换基本类型值的好方法,你没有定义 good 应该意味着什么。对我来说,我认为易于理解的 api 是优秀的一部分。
  • @Ranoiaetep:一旦你知道他在说什么,我们就会意识到他打算将“好”定义为“比通常的方式有更多的优化空间”。 (答案是否定的,更糟)
猜你喜欢
  • 2019-03-14
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-09-01
  • 1970-01-01
  • 2011-07-31
  • 1970-01-01
相关资源
最近更新 更多