GCC属性初始化上首先使用功能 [英] gcc attributes for init-on-first-use functions

查看:306
本文介绍了GCC属性初始化上首先使用功能的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我一直使用gcc的常量纯粹属性,为返回一个指针,以常量功能这是分配,并在第一次使用初始化数据,即那里的功能将每次的调用时返回相同的值。作为一个例子(不是我的使用情况,而是一个著名的例子)认为分配,并计算在第一次通话trig的查找表,只是在第一次调用后返回一个指向现有的表的功能。

I've been using the gcc const and pure attributes for functions which return a pointer to "constant" data that's allocated and initialized on the first use, i.e. where the function will return the same value each time it's called. As an example (not my usage case, but a well-known example) think of a function that allocates and computes trig lookup tables on the first call and just returns a pointer to the existing tables after the first call.

问题:我一直在说这个用法不正确,因为这些属性禁止副作用,编译器甚至可以在某些情况下完全优化了通话,如果不使用返回值。是我的常量的使用 / 纯粹属性安全,或有任何其他方式来告诉<$ C编译器$ C> N'大于1 调用该函数相当于1调用该函数,但1调用函数不等于0调用该函数?或者换句话说,该功能只有副作用第一次,它被称为?

The problem: I've been told this usage is incorrect because these attributes forbid side effects, and that the compiler could even optimize out the call completely in some cases if the return value is not used. Is my usage of const/pure attributes safe, or is there any other way to tell the compiler that N>1 calls to the function are equivalent to 1 call to the function, but that 1 call to the function is not equivalent to 0 calls to the function? Or in other words, that the function only has side effects the first time it's called?

推荐答案

我说这是正确的基础上我的纯粹的和的常量的,但如果任何人有一个认识两个precise定义,请说出来。这得到棘手,因为海湾合作委员会的文件没有规定确切意味着什么功能有除返回值没​​有影响(对的纯粹的),或不检查除它们的参数的任何值(对的常量的)。显然,所有功能都的部分的效果(他们使用的处理器周期,修改内存),并检查的部分的值(函数code,常量)。

I say this is correct based on my understanding of pure and const, but if anyone has a precise definition of the two, please speak up. This gets tricky because the GCC documentation doesn't lay out exactly what it means for a function to have "no effects except the return value" (for pure) or to "not examine any values except their arguments" (for const). Obviously all functions have some effects (they use processor cycles, modify memory) and examine some values (the function code, constants).

副作用将有C编程语言的语义进行定义,但我们​​可以猜出根据这些属性的目的,是为了让更多的优化(至少是海合会人的意思,这就是我认为他们是)。

"Side effects" would have to be defined in terms of the semantics of the C programming language, but we can guess what the GCC folks mean based on the purpose of these attributes, which is to enable additional optimizations (at least, that's what I assume they are for).

原谅我,如果下面的一些过于基本...

Forgive me if some of the following is too basic...

纯函数能参与共同SUBEX pression消除。他们的特点是,他们没有修改环境,所以编译器可以自由地调用它的次数更少不改变程序的语义。

Pure functions can participate in common subexpression elimination. Their feature is that they don't modify the environment, so the compiler is free to call it fewer times without changing the semantics of the program.

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

变成了:

z = y = f(x);

或获取完全消除,如果以Z 未使用。

Or gets eliminated entirely if z and y are unused.

所以,我最好的猜测是纯的工作定义为它可以在不改变程序的语义被调用次数的功能。但是,函数调用可能不被感动,如,

So my best guess is that a working definition of "pure" is "any function which can be called fewer times without changing the semantics of the program". However, function calls may not be moved, e.g.,

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

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

Const functions can be reordered, because they do not depend on the dynamic environment.

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

所以,我最好的猜测是常量的工作定义为可以在任何时候被调用不改变程序的语义任何函数。但是,存在这样的危险:

So my best guess is that a working definition of "const" is "any function which can be called at any point without changing the semantics of the program". However, there is a danger:

__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;
}

由于它是常量,编译器可能重新排序...

Since it's const, the compiler could reorder it...

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

在这种情况下,它可以同时从即使它仅仅出现在code进行临界部分内的两个处理器调用。那么处理器可以决定更改 initted 后推迟修改已经经历了,这是个坏消息。您也可以用记忆障碍解决这个或调用pthread_once

In this case, it could be called simultaneously from two processors even though it only appears inside a critical section in your code. Then the processor could decide to postpone changes to table after a change to initted already went through, which is bad news. You can solve this with memory barriers or pthread_once.

我不认为这个bug将永远显示在x86上,我不认为它显示了在不具有多个物理处理器(没有核心)许多系统。因此,它会正常工作的年龄,然后突然失败了双插座电源的计算机上。

I don't think this bug will ever show up on x86, and I don't think it shows up on many systems that don't have multiple physical processors (not cores). So it will work fine for ages and then fail suddenly on a dual-socket POWER computer.

结论:这些定义的优点是,他们说清楚编译器是允许什么样的变化在这些属性,(我认为)的presence使有些模糊的GCC文档。缺点是,目前还不清楚,这些都是由GCC团队使用的定义。

Conclusion: The advantage of these definitions is that they make it clear what kind of changes the compiler is allowed to make in the presence of these attributes, which (I think is) somewhat vague in the GCC docs. The disadvantage is that it's not clear that these are the definitions used by the GCC team.

如果你看一下Haskell语言规范,例如,你会发现纯度更precise定义,因为纯度是Haskell语言那么重要了。

If you look at the Haskell language specification, for example, you'll find a much more precise definition of purity, since purity is so important to the Haskell language.

编辑:我一直无法强迫GCC或锵成跨越另一个函数移动孤 __属性__((常量))函数调用叫,但似乎完全有可能在将来,类似的东西会发生。还记得 -fstrict走样成为默认的,每个人都在他们的计划突然有很多更多的错误?的它这样的东西,这让我持谨慎态度。

I have not been able to coerce GCC or Clang into moving a solitary __attribute__((const)) function call across another function call, but it seems entirely possible that in the future, something like that would happen. Remember when -fstrict-aliasing became the default, and everybody suddenly had a lot more bugs in their programs? It's stuff like that that makes me cautious.

在我看来,当你标记功能 __属性__((常量)),你是有希望的编译器,函数调用的结果是相同的,无论当你的程序的执行期间被调用时,只要参数是相同的。

It seems to me that when you mark a function __attribute__((const)), you are promising the compiler that the result of the function call is the same no matter when it is called during your program's execution, as long as the parameters are the same.

不过,我没有拿出移动const函数了关键部分的一种方式,虽然我做的方式,它可以被称为一种作弊。

However, I did come up with a way of moving a const function out of a critical section, although the way I did it could be called "cheating" of a sort.

__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;
}

编译器把这个分为以下code(从组件):

The compiler translates this into the following code (from the assembly):

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

请注意,这将不使用只用 __属性__((折纯))常量属性,只发生在常量属性触发此行为。

Note that this won't happen with only __attribute__((pure)), the const attribute and only the const attribute triggers this behavior.

正如你可以看到,在临界区内通话消失。这似乎相当随意,早先的通话保持,我也不会愿意打赌,编译器就不会在将来的某个版本,做出不同的决定,关于哪些来电保存,或者它是否会某处移动函数调用其他类别。

As you can see, the call inside the critical section disappeared. It seems rather arbitrary that the earlier call was kept, and I would not be willing to wager that the compiler won't, in some future version, make a different decision about which call to keep, or whether it might move the function call somewhere else entirely.

结论2:谨慎行事,因为如果你不知道你正在编译器什么承诺,编译器的未来版本可能会让你大吃一惊。

Conclusion 2: Tread carefully, because if you don't know what promises you are making to the compiler, a future version of the compiler might surprise you.

这篇关于GCC属性初始化上首先使用功能的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆