编译期执行basename的方法

前言

最近在review log模块的设计,当时实现这个模块的时候,为了在log中输出源文件名(不包含路径),使用了库函数basename。

#define  log_comm(level, fmt, ...)                      \
            do {                                    \
                if (LOG_LEVEL_##level >= log_level) { \
                    time_t now;                     \
                    time(&now);                     \
                    struct tm local;                \
                    localtime_r(&now, &local);      \
                    struct timeval tim;             \
                    gettimeofday(&tim, NULL);       \
                    log_write_comm(LOG_LEVEL_##level, #level":\t%04d-%02d-%02d %02d:%02d:%02d:%03d: %s:%d: " fmt,   \
                    local.tm_year+1900, local.tm_mon+1, local.tm_mday, local.tm_hour,       \
                    local.tm_min, local.tm_sec, tim.tv_usec/1000,                           \
                    basename(__FILE__), __LINE__, ##__VA_ARGS__);                           \
                }                                   \
            } while (0);

每调用一次log输出都会使用同样的参数__FILE__调用basename,产生相同的返回值,属于浪费的操作,且会影响到日志模块的执行效率。

之前听到过编译期basename函数,可以在编译期间直接将函数调用结果插入到代码中,但当时一直没有找到相关资料,这个问题昨天通过其他方式得到了解决,下面说明下解决思路。

编译器优化

开始以为像这种固定参数传入,返回值也属于固定值的函数调用,编译器能够优化掉其调用过程,直接使用返回值。为了验证这个思路,特意写了一个程序进行验证。

#include <stdio.h>                     
#include <libgen.h>                    
int main(void) {                       
    printf("%s\n", basename(__FILE__));      
    return 0;                          
}                                      

使用-O3的优化级别,查看生成的汇编代码:

gcc -O3 -S /home/yu/temp/base.c
cat base.s
    .file   "base.c"
    .section    .rodata.str1.1,"aMS",@progbits,1
.LC0:
    .string "/home/yu/temp/base.c"
    .section    .text.unlikely,"ax",@progbits
.LCOLDB1:
    .section    .text.startup,"ax",@progbits
.LHOTB1:
    .p2align 4,,15
    .globl  main
    .type   main, @function
main:
.LFB23:
    .cfi_startproc
    subq    $8, %rsp
    .cfi_def_cfa_offset 16
    movl    $.LC0, %edi
    call    __xpg_basename
    movq    %rax, %rdi
    call    puts
    xorl    %eax, %eax
    addq    $8, %rsp
    .cfi_def_cfa_offset 8
    ret 
    .cfi_endproc
.LFE23:
    .size   main, .-main
    .section    .text.unlikely
.LCOLDE1:
    .section    .text.startup
.LHOTE1:
    .ident  "GCC: (Ubuntu 5.4.0-6ubuntu1~16.04.9) 5.4.0 20160609"
    .section    .note.GNU-stack,"",@progbits

basename的系统调用__xpg_basename还是存在,编译器优化没起作用。

宏定义方法

后来找到了另外一种方法,在编译时通过指定宏定义,将短文件名传入(需要Makefile的配合)。为了演示效果,特意在编译时给源文件加入了全路径。

base: base.c
    gcc -D__SHORT_FILE__=\"`basename $<`\"  -S `pwd`/$<

源代码修改为

#include <stdio.h>                     
#include <libgen.h>                    
int main(void) {                       
    printf("%s\n", __SHORT_FILE__);      
    return 0;                          
} 

查看汇编代码

    .file   "base.c"
    .section    .rodata
.LC0:
    .string "base.c"
    .text
    .globl  main
    .type   main, @function
main:
.LFB0:
    .cfi_startproc
    pushq   %rbp
    .cfi_def_cfa_offset 16
    .cfi_offset 6, -16 
    movq    %rsp, %rbp
    .cfi_def_cfa_register 6
    movl    $.LC0, %edi
    call    puts
    movl    $0, %eax
    popq    %rbp
    .cfi_def_cfa 7, 8
    ret 
    .cfi_endproc
.LFE0:
    .size   main, .-main
    .ident  "GCC: (Ubuntu 5.4.0-6ubuntu1~16.04.9) 5.4.0 20160609"
    .section    .note.GNU-stack,"",@progbits

没了系统调用,puts的入参时是“base.c”,达到了在编译期执行basename的同等效果。

修改log宏定义

将basename(FILE) 改为 SHORT_FILE,log模块关于basename调用的优化完毕。

转载于:https://my.oschina.net/yumm007/blog/1649578

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值