首次使用函数的 gcc 属性

2023-12-21

我一直在使用gccconst and pure返回指向首次使用时分配和初始化的“常量”数据的指针的函数的属性,即函数每次调用时将返回相同的值。作为一个示例(不是我的用例,而是一个众所周知的示例),请考虑一个函数,该函数在第一次调用时分配和计算三角查找表,并在第一次调用后仅返回指向现有表的指针。

问题:我被告知这种用法是不正确的,因为这些属性禁止副作用,并且如果不使用返回值,编译器甚至可以在某些情况下完全优化调用。我的用法是const/pure属性安全,或者有其他方法告诉编译器N>1对函数的调用相当于对函数的1次调用,但是对函数的1次调用不等于对函数的0次调用吗?或者换句话说,该函数仅在第一次调用时有副作用?


根据我的理解,我说这是正确的pure and const,但如果有人对两者有准确的定义,请说出来。这变得很棘手,因为 GCC 文档没有准确说明函数“除了返回值之外没有任何影响”的含义(例如pure)或“不检查除参数之外的任何值”(对于const)。显然所有的函数都有some影响(他们使用处理器周期,修改内存)并检查some值(功能代码、常量)。

“副作用”必须根据 C 编程语言的语义来定义,但我们可以根据这些属性的目的猜测 GCC 人员的意思,即实现额外的优化(至少,这就是我的意思)假设它们是为了)。

如果以下某些内容太基础,请原谅我......

纯函数可以参与公共子表达式消除。它们的特点是不修改环境,因此编译器可以自由地调用它更少的次数,而不会改变程序的语义。

z = f(x);
y = f(x);

becomes:

z = y = f(x);

或者被完全淘汰,如果z and y未使用。

所以我最好的猜测是“纯粹”的工作定义是“任何可以在不改变程序语义的情况下调用更少次数的函数”。但是,函数调用可能无法移动,例如,

size_t l = strlen(str); // strlen is pure
*some_ptr = '\0';
// Obviously, strlen can't be moved here...

常量函数可以重新排序,因为它们不依赖于动态环境。

// Assuming x and y not aliased, sin can be moved anywhere
*some_ptr = '\0';
double y = sin(x);
*other_ptr = '\0';

所以我最好的猜测是“const”的工作定义是“可以在任何时候调用而不改变程序语义的任何函数”。然而,存在一个危险:

__attribute__((const))
double big_math_func(double x, double theta, double iota)
{
    static double table[512];
    static bool initted = false;
    if (!initted) {
        ...
        initted = true;
    }
    ...
    return result;
}

由于它是 const,编译器可以对其重新排序......

pthread_mutex_lock(&mutex);
...
z = big_math_func(x, theta, iota);
...
pthread_mutex_unlock(&mutex);
// big_math_func might go here, if the compiler wants to

在这种情况下,即使它只出现在代码的关键部分内,也可以从两个处理器同时调用它。然后处理器可以决定推迟更改table更改为后initted已经经历了,这是坏消息。您可以通过内存屏障或pthread_once.

我认为这个错误不会出现在 x86 上,而且我认为它不会出现在许多没有多个物理处理器(不是核心)的系统上。因此,它可以正常工作很长时间,然后在双插槽 POWER 计算机上突然出现故障。

结论:这些定义的优点是它们清楚地表明编译器在存在这些属性的情况下可以进行什么样的更改,而这(我认为)在 GCC 文档中有些模糊。缺点是不清楚这些是 GCC 团队使用的定义。

例如,如果您查看 Haskell 语言规范,您会发现纯度的定义更加精确,因为纯度对于 Haskell 语言非常重要。

Edit:我无法强迫 GCC 或 Clang 移动一个单独的__attribute__((const))函数调用跨越另一个函数调用,但将来似乎完全有可能发生类似的事情。记得当-fstrict-aliasing成为默认设置,然后每个人的程序中突然出现了更多错误?诸如此类的事情让我变得谨慎。

在我看来,当你标记一个函数时__attribute__((const)),您向编译器承诺,只要参数相同,无论在程序执行期间何时调用该函数,调用的结果都是相同的。

然而,我确实想出了一种将 const 函数移出关键部分的方法,尽管我所做的方法可以被称为某种“作弊”。

__attribute__((const))
extern int const_func(int x);

int func(int x)
{
    int y1, y2;
    y1 = const_func(x);
    pthread_mutex_lock(&mutex);
    y2 = const_func(x);
    pthread_mutex_unlock(&mutex);
    return y1 + y2;
}

编译器将其转换为以下代码(来自程序集):

int func(int x)
{
    int y;
    y = const_func(x);
    pthread_mutex_lock(&mutex);
    pthread_mutex_unlock(&mutex);
    return y * 2;
}

请注意,这不会发生在仅__attribute__((pure)), the const属性并且仅const属性触发此行为。

正如你所看到的,临界区内的调用消失了。保留早期的调用似乎相当任意,而且我不愿意打赌编译器不会在未来的某个版本中就保留哪个调用做出不同的决定,或者是否会将函数调用移动到某个地方完全是别的。

结论2:请小心行事,因为如果您不知道对编译器做出的承诺,编译器的未来版本可能会让您感到惊讶。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

首次使用函数的 gcc 属性 的相关文章

随机推荐

  • 错误 ITMS-9000:“无效架构:包含应用程序扩展和框架的应用程序必须支持 arm64

    我想在 Itunes Connect 上上传带有自定义键盘扩展的版本 但是当我要通过应用程序加载器将其上传到 Itunes Connect 时 它会显示以下错误 ERROR ITMS 9000 Invalid architecture Ap
  • 如何在 SQL 的嵌套子查询中查找不同的列?

    我需要找到订购 伏特加 和 威士忌 的不同饮酒者 我只被允许使用嵌套查询 不接受其他格式 我对 sql 很陌生 所以任何帮助将不胜感激 以下是我尝试从中检索数据的表 CREATE TABLE DRINKERS All drinkers DR
  • restangular:可以有进度条吗?

    我有这个基于 reangular 的代码 在服务中 它有效 sendFile function params console log sendFile console log params return this restfulBase p
  • Coq Proof Assistant 中依赖类型的问题

    考虑以下简单的表达语言 Inductive Exp Set EConst nat gt Exp EVar nat gt Exp EFun nat gt list Exp gt Exp 及其格式良好的谓词 Definition Env lis
  • 设置 gdb 和 eclipse 从 Android 应用程序调试共享库后,无法访问地址 0X1 处的内存

    我有两个 Android 项目 一个是一个库 另一个是使用该库的常规应用程序 我的目标是从 Android 应用程序调试共享库 我遵循了这个指南 http mhandroid wordpress com 2011 01 23 using e
  • smtplib 在 Python 3.1 中使用 unicode 字符发送邮件的问题

    您好 我遇到了 unicode 电子邮件的问题 当我尝试用西班牙语发送 A adir 等单词时 系统崩溃了 我尝试了此链接上的说明 Python 3 smtplib 使用unicode字符发送 https stackoverflow com
  • 无法使用 JAXB 生成类

    我尝试使用 JAXB 生成类 但出现异常 IllegalArgumentException 非法模式字符 g 我使用一个复杂的 XSD 文件和一个绑定文件 如下所示 有人可以提供调查地点的线索吗
  • Python 获取<标题>

    我想获取使用 urllib2 打开的网页的标题 执行此操作的最佳方法是什么 解析 html 并找到我需要的内容 目前只有 标记 但将来可能需要更多 有没有一个好的解析库用于此目的 是的 我会推荐美丽汤 http www crummy com
  • KissFFT(kiss_fftr 到 Kiss_fftri) - 如何重建原始信号?

    我在理解如何正确使用 KissFFT 1 2 9 时遇到一些困难 我现在想要实现的目标是执行 FFT 然后立即执行 iFFT 以再次重建原始信号 下面的代码片段演示了我正在做的事情 void test short timeDomainDat
  • 如何从 bash 脚本中删除注释

    我正在尝试制作一个将脚本文件作为参数的脚本 它应该从文件中删除注释并将其通过管道传输到另一个脚本 如果可能的话 没有临时文件 一开始我想做这个 cut d f1 1 script name 但它也会清除一部分不是注释的行 因为有一些命令在其
  • WCF Net.TCP 服务抛出模糊错误

    我正在尝试为现有 WCF 服务启用额外的 net tcp 端点 使用用户名身份验证 该服务托管在 IIS7 中 针对服务执行客户端会返回套接字连接已中止的错误 启用对服务的跟踪后会发现抛出以下异常 System ArgumentOutOfR
  • 使用反射创建新对象?

    给定类值 public class Value private int xVal1 private int xVal2 private double pVal constructor of the Value class public Va
  • 通过引用传递智能指针

    智能指针通常很小 因此按值传递不是问题 但传递对它们的引用是否有问题 或者更确切地说 是否存在不能这样做的特定情况 我正在编写一个包装器库 并且我的几个类将智能指针对象包装在底层库中 我的类不是智能指针 但 API 目前按值传递智能指针对象
  • 使用 jQuery 设置要在第一个位置显示的选项

    我有一个带有一些选项的选择元素
  • Opera 中的 JavaScript 范围问题?

    我有一个类似于小部件的 HTML JavaScript 块 人们可以将其复制 粘贴到他们的 HTML 页面中一次或多次 该块检查 DOM 中是否已存在外部 JavaScript 文件 如果没有则加载它 如下所示 function d doc
  • 挑战 - 将 XML 处理成 T-SQL 表结构?

    如果您有此 XML
  • 触摸屏上的“滚轮”事件

    我有这个小提琴https jsfiddle net 316n1xmL 1 https jsfiddle net 316n1xmL 1 这在桌面上完美地满足了我的需要 根据滚轮滚动方向向上或向下计数并添加和删除类 我遇到的问题是如何在触摸屏上
  • XmlTextWriter序列化问题

    我正在尝试创建一段 xml 我已经使用 xsd exe 创建了数据类 根类是MESSAGE 所以创建之后MESSAGE并填充其所有属性 我将其序列化如下 serializer new XmlSerializer typeof Xsd MES
  • 未解决的___stdio_common_vsprintf_s,什么库有这个?

    我正在使用 VS2015 社区 我已经获得了一些我正在尝试构建的 C 代码 这一切都是用 C 编写的 我已经制作了一个项目作为控制台应用程序 当我构建时 我收到以下错误 问题是链接过程中找不到 stdio common vsprintf s
  • 首次使用函数的 gcc 属性

    我一直在使用gccconst and pure返回指向首次使用时分配和初始化的 常量 数据的指针的函数的属性 即函数每次调用时将返回相同的值 作为一个示例 不是我的用例 而是一个众所周知的示例 请考虑一个函数 该函数在第一次调用时分配和计算