为什么GCC没有将'printf'优化为'put'?

时间:2017-06-07 07:58:16

标签: c gcc assembly compiler-optimization

这是我的测试代码:

public class Test {

    /**
     * @param args the command line arguments
     */
    static int dest_x = 0;
    static int dest_y = 0;
    static int cost = 0;
    static int m = 0;
    static int n = 0;

    public static void main(String[] args) {
        // TODO code application logic here
        Scanner in = new Scanner(System.in);
        Test rat = new Test();
        int maze[][] = {
                {1, 1, 1, 1},
                {1, 1, 1, 0},
                {1, 1, 1, 1},
                {0, 0, 1, 1}
        };
        dest_x = in.nextInt();
        dest_y = in.nextInt();
        m = in.nextInt();
        n = in.nextInt();
        if (rat.solveMaze(maze))
            System.out.println("YES");

        else {
            System.out.println("NO");
        }
        System.out.println("cost = " + cost);
    }

    boolean isSafe(int maze[][], int x, int y) {
        // if (x,y outside maze) return false
        return (x >= 0 && x < m && y >= 0 &&
                y < n && maze[x][y] == 1);
    }

    boolean solveMaze(int maze[][]) {
        int sol[][] = {{0, 0, 0, 0},
                {0, 0, 0, 0},
                {0, 0, 0, 0},
                {0, 0, 0, 0}
        };

        if (!solveMazeUtil(maze, 0, 0, sol)) {
            return false;
        }
        return true;
    }

    boolean solveMazeUtil(int maze[][], int x, int y, int sol[][]) {
        // if (x,y is goal) return true
        if (x <= dest_x || y <= dest_y)
            if (x == dest_x && y == dest_y) {
                sol[x][y] = 1;
                return true;
            } else if (sol[x][y] == 1) {
                return false;
            }

            // Check if maze[x][y] is valid
            else if (isSafe(maze, x, y)) {
                // mark x,y as part of solution path
                sol[x][y] = 1;

                // Move forward in x direction 
                if (solveMazeUtil(maze, x + 1, y, sol)) {
                    //System.out.println("here at x+1 x = " + x  + " y = " + y);
                    return true;
                }

                // Move down in y direction 
                if (solveMazeUtil(maze, x, y + 1, sol)) {
                    //System.out.println("here at y+1 x = " + x  + " y = " + y);
                    cost++;
                    return true;
                }
                cost--;
                if (solveMazeUtil(maze, x - 1, y, sol)) {
                    // System.out.println("here at x-1 x = " + x  + " y = " + y);  
                    return true;
                }
                if (solveMazeUtil(maze, x, y - 1, sol)) {
                    //System.out.println("here at y-1 x = " + x  + " y = " + y);  
                    cost++;
                    return true;
                }
                cost--;
                /* If no solution then
                   BACKTRACK: unmark x,y as part of solution
                   path */
                sol[x][y] = 0;
                return false;
            }
        return false;
    }
}

我认为GCC应该意识到#include<stdio.h> static inline void foo(int a){ printf("%x\n", a); } int main(void){ foo(0x1234); return 0; } 是一个文字整数,并优化为这样的代码:

a

但是我得到了以下汇编代码:

puts("1234");

我的项目中存在很多这样的代码,因为我一直认为GCC会为我优化,甚至在某些可以简单地使用'write()'的上下文中,我坚持使用│0x8048341 <main+17> push $0x1234 │0x8048346 <main+22> push $0x80484e0 │0x804834b <main+27> push $0x1 │0x804834d <main+29> call 0x8048310 <__printf_chk@plt> ,因为我以为我会从缓冲机制中获益。

现在我感到遗憾,因为削减格式字符串的开销会杀死我所获得的任何收益。我项目中的这些代码非常低级,可能会导致性能瓶颈。

2 个答案:

答案 0 :(得分:6)

  

我项目中的这些代码非常低级,可能会导致性能瓶颈。

首先,我可以平息你担心这是不可能的。控制台I / O的开销是巨大的(相对而言),因此无论您使用什么方式,它始终是代码中的瓶颈。

  

我认为gcc应该意识到a是一个文字整数,并优化为这样的代码:

puts("1234");

显然它没有。 GCC(和Clang)does perform an optimization where printf("...\n"); is transformed into puts("...");,正如您可以看到here,但只有当您将字符串文字printf一起使用时才会发生这种情况。优化器没有(当前)查看格式字符串,解析它并围绕它进行优化。您致电printf,因此获得printf

编译器优化不是保证,因此您不应该首先编写依赖的代码,而不首先验证所需的优化实际上是适用于您感兴趣的所有情况(包括代码变体,编译器版本,目标平台等)。

如果您想将此建议作为GCC优化工具的可能改进,您可以建议their Bugzilla的增强功能。但是,不要在短时间内实施它。实现这种类型的优化所需的逻辑并不值得付出努力,考虑到可以预期的实际性能改进最多是最小的(见上文)。

与此同时,如果您绝对需要对代码进行最少的更改而进行此优化,那么您可以使用some macro hackery

#define STRINGIFY_INTERNAL(x)  #x
#define STRINGIFY(x)           STRINGIFY_INTERNAL(x)

#define foo(a)                 puts(STRINGIFY(a))

这确实产生了所需的输出:

.LC0:
        .string "0x1234"
MyFunction:
        sub     esp, 24
        push    OFFSET FLAT:.LC0
        call    puts
        xor     eax, eax
        add     esp, 28
        ret

答案 1 :(得分:1)

符合标准的库实现可以包括标准定义的函数之外的函数,这将改变标准函数的行为方式。例如,库可能包含__select_alternate_digits函数,在调用时,会导致对printf的后续调用使用非正常数字显示数字。

使用这样的库,给出代码:

#include <stdio.h> // Could legitimately include functions that aren't
                  // defined by the Standard, but which start with __.

int main(void)
{
  __select_alternate_digits("⁰¹²³⁴⁵⁶⁷⁸⁹");
  printf("%d",123);
  __select_alternate_digits(0); // Reset to default set      
}

__select_alternate_digits的调用可能导致上述程序输出“³²³”而不是“123”。如果编译器捆绑了自己的printf函数,它可能知道它的行为不会受到任何其他函数调用的影响。但是,如果它正在使用外部库,那么除非程序完全不需要编译器一无所知的函数调用,否则质量编译器应该假定这些函数可能具有编译器无法预测的效果。