10

My basic situation: I have an include file that has something like #define foo (Flag1 | Flags2 | Flag3), so it's a predefined combination of bit flags. For the sake of type-safety, I wanted to replace these #defines by static consts, i.e. static const int foo = (Flag1 | Flag2 | Flag3) (or similar). This include file is included in dozens of places in the program.

Now when I'm doing a release build with all relevant optimisation options enabled (using the C++ compiler of VS2010), replacing the #defines seems to increase the executable by a few KiB, depending on how many constants I replaced.

Why does this happen? To my knowledge, integer constants are supposed to be "inlined" into the ASM code that is produced if possible, and I don't see how using a static const vs #define would make a difference here. Clearly, the variable isn't inlined as the disassembly shows:

#define:
01325041  or          eax,0FFD87FE0h
static int:
011E5451  or          eax,dword ptr [CMainFrame::s_TemplateModulePaths+38h (151F008h)]

So the final question is: How can I avoid #define but still rely on the variable being inserted directly into the generated assembly?

4

5 回答 5

4

There is no reason the compiler couldn't eliminate the static const variable. If you're compiling with optimizations turned on, I'm surprised that VC++ doesn't do that.

I tried compiling this code with gcc.

enum { FLAG1 = 1 << 0, FLAG2 = 1 << 1, FLAG3 = 1 << 2 };

static const int foo = (FLAG1 | FLAG2 | FLAG3);

int main(){
    return foo;
}

With optimizations turned off, it inlined the value but still reserved storage space for the variable.

_main:
LFB0:
    .cfi_startproc
    pushl   %ebp
    .cfi_def_cfa_offset 8
    .cfi_offset 5, -8
    movl    %esp, %ebp
    .cfi_def_cfa_register 5
    andl    $-16, %esp
    call    ___main
    movl    $7, %eax  ;value inlined
    leave
    .cfi_restore 5
    .cfi_def_cfa 4, 4
    ret
    .cfi_endproc
LFE0:
    .section .rdata,"dr"
    .align 4
__ZL3foo:                     ; storage space for foo
    .long   7

At O2 it inlined the value and got rid of the storage space.

_main:
LFB0:
    .cfi_startproc
    pushl   %ebp
    .cfi_def_cfa_offset 8
    .cfi_offset 5, -8
    movl    %esp, %ebp
   .cfi_def_cfa_register 5
    andl    $-16, %esp
    call    ___main
    movl    $7, %eax    ; value inlined
    leave
    .cfi_restore 5
    .cfi_def_cfa 4, 4
    ret
   .cfi_endproc
LFE0:
; no storage space for foo

My only thought other that incorrect compiler settings, or VC++ failing to make this optimization, is that the Flags variables might not be compile-time constants which would mean that the value of the expression has to be computed at program start-up, which would prevent inlining.

于 2012-08-02T14:49:06.183 回答
0

As seen in the comments, the typesafe operator| overloading for my enums seems to prevent VC++ from inlining the ORed value. I guess I'll keep using the #define version as I hate increasing the executable size if there's no benefits (no, this is not premature optimization) - after all, it doesn't increase readability, and since the combination of flags is already of my flagset enum type, I also don't lose any type-safety, I guess.

于 2012-08-03T12:20:47.543 回答
0
#define ERROR_1 int(1)
#define ERROR_2 int(2)

etc...

and then, you'll have the value directly in the instruction, and you will also have type checking :) and without any optimisation parameter.

In my opinion, the static const int BLABLA=1; stuff is only there to add a namespace to some constants... and use more ram and ram access in the code, what is really pointless when the symbol for a #define constant have to be unique, and when the reference to an error code in memory is less consistent (any malware/bug can modify these values in ram during run time) than the immediate value of this error code, directly in the instruction.

But it is more about modern programming politics... some will claim, use public static const int, and others (like me and Microsoft) prefer the simpler version #define.

于 2014-03-18T13:18:38.757 回答
-2

You can solve your problem by looking at it from another perspective: you may put the data and the functions that use it close together.

In an OO language, this means making your constant foo a private static attribute of a class. This class should have as public methods all the functions that use foo. Being a static private attribute, it will be defined in the .cpp file, and there will be just one instance of it in the executable footprint.

I understand this would be a big change in your code, and not a simple refactorying: surely it's not as simple as modifying the definition of foo from #define to a static global constant. But keeping data and methods that use them close together (i.e.: in the same class) will bring you many non-functional advantages.

于 2012-08-02T14:35:49.043 回答
-2

My guess is that you have static const int foo = (Flag1 | Flag2 | Flag3) in a header file. This causes a version of the constant to be stored in every object file separately. The #define embeds the literal which are all combined by pooling.

Try this, in one C++ file have const int foo = (Flag1 | Flag2 | Flag3), then in the header have extern const int foo.


Update

Sorry if my answer didn't suit your needs. I addressed the question of file size for static const variables.

I guess it's all in what you want. Unless a section of code is causing performance issues, I would never worry about this kind inline of optimization. For me, type safety and file size are things I value above CPU optimization. In addition, an extern const allows for better analysis by coverage and timing tools.

Premature optimization is the root of all evil (or at least most of it) in programming.

Donald Knuth

于 2012-08-02T14:36:42.053 回答