gcc 최적화레벨 로그함수

qiiiiiiiip의 이미지


아래 코드입니다.

$ cat test.c
#include <stdio.h>
#include <math.h>
 
typedef union {
    float f;
    unsigned int d;
} floatint;
 
int main()
{
    floatint x;
    float y;
 
    x.d = 0x41CB6496;
 
    y = (float)log( (double)(x.f) );
 
    fprintf( stdout, "x= %20.15f y= %20.15f\n", x.f, y );
 
    return 0;
}

이코드를 돌리면 -O옵션에 관계없이,

x= 25.424114227294922 y= 3.235697984695435

가 나옵니다. 그런데 동일한 코드가 전체 프로그램에 들어가면
y값이 3.235698223114014 로 살짝 다르게 나옵니다. (-O1이상에서.. )
전체 몇백개의 어레이엘리먼트 중에 단 하나만..

위코드와 차이점은 앞부분에 복잡한 계산을 더하고,
로그의 입력값이 배열에 들어있다는 정도입니다.

테스트를 위해서 루프도 안돌리고 딱 그 엘리먼트 하나만
따로 떼서 돌려봐도 동일하게 3.235698223114014 가 나오네요..

어떤 경우에 이럴수 있는지요?
앞부분의 메모리 corruption이 optimization level에 따라 뒷부분에 영향을 준다고 봐야하나요?
valgrind로 따로 나오는 것은 없었습니다..

qiiiiiiiip의 이미지

재현이 되었습니다...
아래 첨부한 .dat 파일을 읽어서 아래코드처럼
로그계산을 해보면 -O1과 -O0가 다른 결과가 나오네요..
왜 그럴까요? 이걸 동일하게 할 수 있는 옵션이 있는지요?

#include <stdio.h>
#include <math.h>
#include <stdlib.h>
 
int main()
{
    float* in = (float*)malloc(sizeof(float)*(24));
 
    FILE* fp = fopen( "in.dat", "rb" );
    fread( in, sizeof( float ), 24, fp );
    fclose( fp );
 
    fprintf( stderr, "in_before = %20.15f\n", in[1] );
    in[1] = (float)( log( (double)in[1] ) );
    fprintf( stderr, "in_after = %20.15f\n", in[1] );
 
    return 0;
}

댓글 첨부 파일: 
첨부파일 크기
Binary Data in.dat_.gz126바이트
런맨의 이미지

[root@localhost log_test]# gcc -O0 s.c -lm
[root@localhost log_test]# ./a.out
in_before = 25.424114227294922
in_after = 3.235697984695435
[root@localhost log_test]# gcc -O1 s.c -lm
[root@localhost log_test]# ./a.out
in_before = 25.424114227294922
in_after = 3.235697984695435
[root@localhost log_test]#

인생은 도박이다.

qiiiiiiiip의 이미지

그러게요.. 이상하네요...

저는 두가지 시스템에서 모두 동일하게,
두 값이 서로 다르게 나오던데요..

$ gcc -v
Using built-in specs.
COLLECT_GCC=gcc
COLLECT_LTO_WRAPPER=/usr/lib/gcc/x86_64-unknown-linux-gnu/4.7.1/lto-wrapper
Target: x86_64-unknown-linux-gnu
Configured with: /build/src/gcc-4.7.1/configure --prefix=/usr --libdir=/usr/lib --libexecdir=/usr/lib --mandir=/usr/share/man --infodir=/usr/share/info --with-bugurl=https://bugs.archlinux.org/ --enable-languages=c,c++,ada,fortran,go,lto,objc,obj-c++ --enable-shared --enable-threads=posix --with-system-zlib --enable-__cxa_atexit --disable-libunwind-exceptions --enable-clocale=gnu --disable-libstdcxx-pch --enable-libstdcxx-time --enable-gnu-unique-object --enable-linker-build-id --with-ppl --enable-cloog-backend=isl --enable-lto --enable-gold --enable-ld=default --enable-plugin --with-plugin-ld=ld.gold --with-linker-hash-style=gnu --enable-multilib --disable-libssp --disable-build-with-cxx --disable-build-poststage1-with-cxx --enable-checking=release --with-fpmath=sse
Thread model: posix
gcc version 4.7.1 (GCC)

$ gcc -v
Using built-in specs.
Target: x86_64-redhat-linux
Configured with: ../configure --prefix=/usr --mandir=/usr/share/man --infodir=/usr/share/info --enable-shared --enable-threads=posix --enable-checking=release --with-system-zlib --enable-__cxa_atexit --disable-libunwind-exceptions --enable-libgcj-multifile --enable-languages=c,c++,objc,obj-c++,java,fortran,ada --enable-java-awt=gtk --disable-dssi --enable-plugin --with-java-home=/usr/lib/jvm/java-1.4.2-gcj-1.4.2.0/jre --with-cpu=generic --host=x86_64-redhat-linux
Thread model: posix
gcc version 4.1.2 20080704 (Red Hat 4.1.2-48)

bushi의 이미지

#include <stdio.h>
#include <float.h>
#include <math.h>
 
int main(int argc, char **argv)
{
        float f = FLT_MAX;
        return printf("%e\n", sqrt(f * f));
}

gcc 로 컴파일 할 때, O1 이상에서 위의 코드는 math 라이브러리를 링크하지 않아도 컴파일됩니다.
f 를 volatile 로 선언할 경우엔 math 라이브러리를 링크해야 합니다.

전에 한 번 애먹은 적이 있습니다.
gcc 의 최적화 과정에서 사용된 내장(?) math 와 실제 타겟 시스템에 있는 libc 의 math 가 달라서요.

qiiiiiiiip의 이미지

답글 감사합니다.. 말씀해주신 내용을 바탕으로,
원인을 파악하게 되었습니다..
math lib.를 링크하지 않고 컴파일을 해보았습니다.

$ gcc a.c
/tmp/ccwjDQsg.o: In function `main':
a.c:(.text+0x82): undefined reference to `log'
collect2: ld returned 1 exit status

$ gcc a.c -O1
/tmp/cceqMrUT.o: In function `main':
a.c:(.text+0x64): undefined reference to `logf'
collect2: ld returned 1 exit status

-O1에서는 log함수대신 logf함수를 사용하려고 하네요.
최적화옵션을 주면서
(double) casting이 무시되고, 기존 double log( double )대신,
float logf( float )가 사용되는것 같네요..

일단 변수를 실제 double변수에 담았다가,
적용하니, 동일하게 나왔습니다..

그런데 이것을 컴파일러 옵션에서 조정할 수는 없을까요?

전웅의 이미지

-fno-builtin
-fno-builtin-log (log 함수만)

이 해결해 줄 것 같습니다.

gcc 는 fp 연산 관련해 좀 엉망이죠. 말 안 듣는 아이 같은...

--
Jun, Woong (woong at gmail.com)
http://www.woong.org

qiiiiiiiip의 이미지

감사합니다. 정확히 제가 찾던 내용입니다.
옵션을 넣어서 잘 동작합니다.

manual page 를 읽어보긴 했는데, 안보이다가, 이제서야 보이네요..

익명 사용자의 이미지

-ffast-math
Sets -fno-math-errno, -funsafe-math-optimizations, -fno-trapping-math, -ffinite-math-only, -fno-rounding-math and -fno-signaling-nans.

This option causes the preprocessor macro "__FAST_MATH__" to be defined.

This option should never be turned on by any -O option since it can result in incorrect output for programs which depend on an exact implementation of IEEE or ISO rules/specifications for math functions.

참고하세요.

qiiiiiiiip의 이미지


위 설명에 있듯이, " never be turned on by any -O option "
라서 -O 와는 별개인 것으로 보이고,
fast-math 옵션을 따로 주지 않았습니다.

-fno-fast-math를 명시적으로 써줘도 동일하게 (잘못된) 결과가 나오네요..

kukyakya의 이미지

gcc는 많은 표준 함수를 builtin으로 지원하고 있습니다.

http://gcc.gnu.org/onlinedocs/gcc-4.7.1/gcc/Other-Builtins.html#Other-Builtins 에서 목록을 확인하실 수 있고,

log 또한 builtin function으로 제공되므로 최적화를 수행했을 때 컴파일 타임에 연산을 해 그 결과값을 바로 사용하는데, math library의 log 구현과 달라 최적화 레벨에 따라 다른 결과값을 나타내는 것으로 보입니다.

klyx의 이미지

질문과는 좀 다른 이야기인데, 32비트 부동소수점 자료형인 float의 경우 (이미 알고 계실것 같지만) 유효자리수는 6자리뿐입니다.
어떠한 경우에도 보장되는 유효자리수 내에서는 동일한 결과를 보여주고 있으므로 최적화가 들어가든 안들어가든 아무런 문제가 없는 것 같은데, 굳이 유효자리수 이후의 숫자를 신경쓰시는 특별한 이유가 있으신가요?

qiiiiiiiip의 이미지

아.. 그런가요?
잘 몰랐던 내용입니다..

사실 그 아래자리가 전체 시스템의 퍼포먼스에 영향을 주지는 않습니다만,

여러사람이 사용하면서 결과물을 바이너리 수준에서 동일하게 맞추는 것이,
몇가지 검증 측면에서 좋은 면이 있어서요..

좋은 정보 감사드립니다.

익명 사용자의 이미지

참고로 몇글자 더 적어보자면..

컴파일러는 믿을게 못되는 녀석입니다. -O5 혹은 -O6 정도로 옵티마이징 옵션을 주면
그 누구도 결과가 어떻게 나오리라 예상하기 어렵습니다.

바꿔말하면, 컴파일러는 항상 컴파일 과정의 최적화에만 관심이 있지,
실행결과가 어떻게 나오는지에 대해서는 전혀 신경쓰지 않습니다.

실행결과가 항상 동일하게 나오게 만들기 위해서는 컴파일러가 최적화 하려는 루틴들에 대해서
상항 예외규정을 적어줘야 합니다.

그래서 대부분의 경우 최적화 옵션은 -O2, 아니면 -O3 정도로 끝납니다.

정말 -O5, -O6 을 쓴 경우는 거의 미친짓에 해당합니다.

아무것도 모르는 왕초보 이거나.. 그게 아니라면
한개의 프로그램만 10년쯤 개발해서 컴파일러가 내가 작성한 코드에 대해서
무슨짓을 할지 훤히 다 아는 베테랑이거나..

kukyakya의 이미지

aggressive하게 최적화를 수행할 경우 의도치 않은 결과가 나올 가능성이 있다는 점에 대해서는 동의합니다만,

major compiler중에 -O4 이상의 최적화 옵션을 지원하는 컴파일러가 있나요?

gcc와 icc의 경우 -O3가 최고레벨인 것으로 알고 있고, 두 컴파일러 모두 -O4 이상의 옵션이 -O3와 동일한 것으로 알고 있습니다.

익명 사용자의 이미지

예전에 pgcc (펜티엄 gcc)라는 녀석이 있었습니다.
인텔 펜티엄CPU가 나온지 얼마 되지 않았던 때였는데..

펜티엄에 최적화되어 쬐금 더 성능이 나와주는 컴파일러를 쓰는 배포판도 있었죠.
배포판 이름도 잘 기억이 안나는군요. 하도 오래되서..-_-;
여기서 -O5, -O6 최적화 옵션을 썼던걸로 기억됩니다.

-O6 옵션을 써서 컴파일했던 프로그램은 데이터베이스 서버였는데..
PostgreSQL초기 버전이었을겁니다. <- 이건 확실하게 기억합니다.
그때 뭔가 꼬여서 아주 힘들었던 기억이납니다.

댓글 달기

Filtered HTML

  • 텍스트에 BBCode 태그를 사용할 수 있습니다. URL은 자동으로 링크 됩니다.
  • 사용할 수 있는 HTML 태그: <p><div><span><br><a><em><strong><del><ins><b><i><u><s><pre><code><cite><blockquote><ul><ol><li><dl><dt><dd><table><tr><td><th><thead><tbody><h1><h2><h3><h4><h5><h6><img><embed><object><param><hr>
  • 다음 태그를 이용하여 소스 코드 구문 강조를 할 수 있습니다: <code>, <blockcode>, <apache>, <applescript>, <autoconf>, <awk>, <bash>, <c>, <cpp>, <css>, <diff>, <drupal5>, <drupal6>, <gdb>, <html>, <html5>, <java>, <javascript>, <ldif>, <lua>, <make>, <mysql>, <perl>, <perl6>, <php>, <pgsql>, <proftpd>, <python>, <reg>, <spec>, <ruby>. 지원하는 태그 형식: <foo>, [foo].
  • web 주소와/이메일 주소를 클릭할 수 있는 링크로 자동으로 바꿉니다.

BBCode

  • 텍스트에 BBCode 태그를 사용할 수 있습니다. URL은 자동으로 링크 됩니다.
  • 다음 태그를 이용하여 소스 코드 구문 강조를 할 수 있습니다: <code>, <blockcode>, <apache>, <applescript>, <autoconf>, <awk>, <bash>, <c>, <cpp>, <css>, <diff>, <drupal5>, <drupal6>, <gdb>, <html>, <html5>, <java>, <javascript>, <ldif>, <lua>, <make>, <mysql>, <perl>, <perl6>, <php>, <pgsql>, <proftpd>, <python>, <reg>, <spec>, <ruby>. 지원하는 태그 형식: <foo>, [foo].
  • 사용할 수 있는 HTML 태그: <p><div><span><br><a><em><strong><del><ins><b><i><u><s><pre><code><cite><blockquote><ul><ol><li><dl><dt><dd><table><tr><td><th><thead><tbody><h1><h2><h3><h4><h5><h6><img><embed><object><param>
  • web 주소와/이메일 주소를 클릭할 수 있는 링크로 자동으로 바꿉니다.

Textile

  • 다음 태그를 이용하여 소스 코드 구문 강조를 할 수 있습니다: <code>, <blockcode>, <apache>, <applescript>, <autoconf>, <awk>, <bash>, <c>, <cpp>, <css>, <diff>, <drupal5>, <drupal6>, <gdb>, <html>, <html5>, <java>, <javascript>, <ldif>, <lua>, <make>, <mysql>, <perl>, <perl6>, <php>, <pgsql>, <proftpd>, <python>, <reg>, <spec>, <ruby>. 지원하는 태그 형식: <foo>, [foo].
  • You can use Textile markup to format text.
  • 사용할 수 있는 HTML 태그: <p><div><span><br><a><em><strong><del><ins><b><i><u><s><pre><code><cite><blockquote><ul><ol><li><dl><dt><dd><table><tr><td><th><thead><tbody><h1><h2><h3><h4><h5><h6><img><embed><object><param><hr>

Markdown

  • 다음 태그를 이용하여 소스 코드 구문 강조를 할 수 있습니다: <code>, <blockcode>, <apache>, <applescript>, <autoconf>, <awk>, <bash>, <c>, <cpp>, <css>, <diff>, <drupal5>, <drupal6>, <gdb>, <html>, <html5>, <java>, <javascript>, <ldif>, <lua>, <make>, <mysql>, <perl>, <perl6>, <php>, <pgsql>, <proftpd>, <python>, <reg>, <spec>, <ruby>. 지원하는 태그 형식: <foo>, [foo].
  • Quick Tips:
    • Two or more spaces at a line's end = Line break
    • Double returns = Paragraph
    • *Single asterisks* or _single underscores_ = Emphasis
    • **Double** or __double__ = Strong
    • This is [a link](http://the.link.example.com "The optional title text")
    For complete details on the Markdown syntax, see the Markdown documentation and Markdown Extra documentation for tables, footnotes, and more.
  • web 주소와/이메일 주소를 클릭할 수 있는 링크로 자동으로 바꿉니다.
  • 사용할 수 있는 HTML 태그: <p><div><span><br><a><em><strong><del><ins><b><i><u><s><pre><code><cite><blockquote><ul><ol><li><dl><dt><dd><table><tr><td><th><thead><tbody><h1><h2><h3><h4><h5><h6><img><embed><object><param><hr>

Plain text

  • HTML 태그를 사용할 수 없습니다.
  • web 주소와/이메일 주소를 클릭할 수 있는 링크로 자동으로 바꿉니다.
  • 줄과 단락은 자동으로 분리됩니다.
댓글 첨부 파일
이 댓글에 이미지나 파일을 업로드 합니다.
파일 크기는 8 MB보다 작아야 합니다.
허용할 파일 형식: txt pdf doc xls gif jpg jpeg mp3 png rar zip.
CAPTCHA
이것은 자동으로 스팸을 올리는 것을 막기 위해서 제공됩니다.