최적화 관련 질문입니다..

terra의 이미지

안녕하세요.. 매일 와서 글 읽기만 했지,
처음으로 글을 올리는 것이라, 조금 긴장이 되네요.

지금 만들고 있는 프로그램이 너무 느려서 최적화를 진행하고 있는중에,
이런것이 컴파일러에 의해서 최적화가 되는지 궁금해서 질문 올립니다.

1. memcmp(buf, "hello world", strlen("hello world")) 
2. memcmp(buf, "hello world", 11)

지금 코드가 1번 처럼 되어 있는것이 많은데 이것을 2번으로 바꾸게 되면,
가독성이나.. 문자 개수를 세는데 오류가 발생할 가능성도 있고.. 아무튼 조금 안좋은것 같구요..
그렇지만 이것이 최적화가 되지 않는다면 2번으로 바꿔야 할것 같습니다.
위와 비슷한 연산들에 대해서 자동으로 컴파일러가 최적화를 해 주는지 궁금합니다.

Necromancer의 이미지

strlen()에서 문자 갯수 세는 작업을 하지 않기 때문에 속도
빨라지죠.

하지만 이걸 컴파일러 최적화라고 보기는 무리입니다.
사용자가 불필요한 연산을 최대한 줄이는 알고리즘을 적용한 것이지.

하지만 원래 길이가 일정치 않은 문자열을 받아들여야 하는 경우에는
strlen()을 써야겠죠.

Written By the Black Knight of Destruction

익명 사용자의 이미지

strcmp 쓰면 안되요?

지리즈의 이미지

minzkn wrote:
strcmp 쓰면 안되요?

strcpy,strncpy를 말씀하시려던 것은 아니신지?

단 hello world에 \0이 들어가지 않는다는 전제 조건하에 ㅋㅋ
그럼 strlen도 소용없나? ㅋㅋ
전에 이런 오류를 범하시는 분이 기억이나서..

There is no spoon. Neo from the Matrix 1999.

ihavnoid의 이미지

terra wrote:
안녕하세요.. 매일 와서 글 읽기만 했지,
처음으로 글을 올리는 것이라, 조금 긴장이 되네요.

지금 만들고 있는 프로그램이 너무 느려서 최적화를 진행하고 있는중에,
이런것이 컴파일러에 의해서 최적화가 되는지 궁금해서 질문 올립니다.

1. memcmp(buf, "hello world", strlen("hello world")) 
2. memcmp(buf, "hello world", 11)

지금 코드가 1번 처럼 되어 있는것이 많은데 이것을 2번으로 바꾸게 되면,
가독성이나.. 문자 개수를 세는데 오류가 발생할 가능성도 있고.. 아무튼 조금 안좋은것 같구요..
그렇지만 이것이 최적화가 되지 않는다면 2번으로 바꿔야 할것 같습니다.
위와 비슷한 연산들에 대해서 자동으로 컴파일러가 최적화를 해 주는지 궁금합니다.

strcmp나 strncmp는 안되고요?

그러나 제 생각에는 1이 2로 최적화되지는 않을 것 같습니다.

Consider the ravens: for they neither sow nor reap; which neither have storehouse nor barn; and God feedeth them: how much more are ye better than the fowls?
Luke 12:24

alfalf의 이미지

'strcmp'에 대해 제가 올린글은 아니지만...

뭐 그냥 확인해 보니...

Quote:
STRCMP(3) Linux Programmer's Manual STRCMP(3)

NAME
strcmp, strncmp - compare two strings

SYNOPSIS
#include <string.h>

int strcmp(const char *s1, const char *s2);

int strncmp(const char *s1, const char *s2, size_t n);

DESCRIPTION
The strcmp() function compares the two strings s1 and s2. It returns an integer less than, equal to, or
greater than zero if s1 is found, respectively, to be less than, to match, or be greater than s2.

The strncmp() function is similar, except it only compares the first n characters of s1.

RETURN VALUE
The strcmp() and strncmp() functions return an integer less than, equal to, or greater than zero if s1
(or the first n bytes thereof) is found, respectively, to be less than, to match, or be greater than s2.

CONFORMING TO
SVID 3, POSIX, BSD 4.3, ISO 9899

SEE ALSO
bcmp(3), memcmp(3), strcasecmp(3), strncasecmp(3), strcoll(3)

strcmp 맞는거 같네요...

지리즈의 이미지

지리즈 wrote:
minzkn wrote:
strcmp 쓰면 안되요?

strcpy,strncpy를 말씀하시려던 것은 아니신지?

단 hello world에 \0이 들어가지 않는다는 전제 조건하에 ㅋㅋ
그럼 strlen도 소용없나? ㅋㅋ
전에 이런 오류를 범하시는 분이 기억이나서..

죄송합니다. memcpy로 잘못 보았습니다.

There is no spoon. Neo from the Matrix 1999.

lsj0713의 이미지

1. memcmp(buf, "hello world", strlen("hello world")) 
2. memcmp(buf, "hello world", 11)

컴파일러 나름이겠지만, 1번 문장을 2번 문장과 동일하게 컴파일할 정도의 지능적인(?) 컴파일러가 있을지는 의문입니다.

둘 중의 어떤 코드가 더 나을지는 위의 코드만 보고 결정하기는 어렵습니다. 하지만 속도가 매우 매우 중요하다면 이런 방법은 어떨까요?

#define STR1 "hello world"
#define STR1_LEN 11

/* ... */

memcmp(buf, STR1, STR1_LEN);

그리고 문자열의 비교라면 memcmp 보다는 strncmp를 사용하기를 권장합니다. C99 표준문서의 7.21.4.1 The memcpy function 항목에서는 다음과 같이 경고하고 있습니다.

"Strings shorter than their allocated space and unions may also cause problems in comparison."

strncmp는 '\0' 문자 이전까지 비교하는데 비해서 memcmp는 무조건 지정된 갯수의 character를 비교합니다. 이로 인해서, 지정된 길이보다 짧은 문자열들을 서로 비교할 경우 memcmp는 같은 내용의 문자열임에도 그 뒤의 쓰레기값으로 인하여 서로 다르다는 판정을 내릴 수 있습니다.

자세한 것은 아래 링크의 글을 읽어보시길 바랍니다.

http://groups.google.co.kr/groups?threadm=6ddtp3%245hv%241%40news.utdallas.edu

sliver의 이미지

$ cat > strlen.c
int main()
{
        return strlen("hello world");
}
$ gcc strlen.c -S -O3
$ cat strlen.s
        .file   "strlen.c"
        .version        "01.01"
gcc2_compiled.:
.text
        .align 4
.globl main
        .type    main,@function
main:
        pushl   %ebp
        movl    $11, %eax 
        movl    %esp, %ebp
        popl    %ebp
        ret
.Lfe1:
        .size    main,.Lfe1-main
        .ident  "GCC: (GNU) 2.96 20000731 (Red Hat Linux 7.3 2.96-110)"

-O3 옵션을 주니 strlen을 실제로 호출하는 대신, 컴파일 단계에서 미리 길이를 계산해서 상수로 대체시키는군요.
따라서 둘 중 고르라면 가독성이 좋은 1번이 나을거 같습니다.
하지만 윗분들 말씀대로 문자열 비교시에는 strcmp/strncmp가 memcmp보다는 더 좋은 방법입니다.

익명 사용자의 이미지

이거참 중대한 결함이 있는 코드를 사용하실거 같아서 한가지 덧붙입니다.

만약 memcmp를 쓰시고자 한다면
이것을 생각해보세요.

int CmpToken(const char *s_Left, const char *s_Right)
{
 return(memcmp(s_Left, s_Right, strlen(s_Right));
}

이것은 다음과 같은 예외를 아주 멋지게 통과하는 버그가 있습니다.
if( CmpToken( "Hello world !", "Hello world" ) == 0)printf("OK\n");
else printf("ERROR\n");

무슨예기인지 이해가십니까?
실제로 memcmp 사용시 주의할 사항은 이점입니다.

int CmpToken(const char *s_Left, const char *s_Right)
{
 return(memcmp(s_Left, s_Right, strlen(s_Right) + 1);
}

이것이 올바른 코드입니다.
사실상 memcmp는 뒤에 길이만큼을 무조건 전부 비교하지 않습니다.
"같은동안 길이를 비교" 라는 뜻이 맞을겁니다.

때문에 어느한쪽이라도 그 문자열이 고정된 상수라면
문제가 발생할 이유가 없습니다.

하지만 양쪽 비교 대상이 가변이라면 반드시 memcmp는 좋은 방법은
아닐겁니다.

xfmulder의 이미지

strlen("hello, world") 와 11
당연히 11 상수를 주는게 빠르지 않을까요?

strlen() 연산을 하려면 11번 if 문장이 11번 들어가야 할텐데 그냥 11 하면 되쟎아요. 가독성이 떨어진다면 #define 도 괜챦은 방법같고.

또 strncmp 보다는 memcmp 가 훨씬 더 빠를것 같은데,
왜냐면 memcmp()는 한 바이트씩 비교해 가다가 최대로 정해진 바이트 수만큼 비교하는데 비해
strncmp 는 매 한 바이트마다 같은지 비교하고 또 0x00 인지도 일일이 확인하게 되므로 연산속도가 느려질것 같네요.

내 자식들도 나처럼 !!

macgebi의 이미지

처음 질문을 올리신 분께서 원하셨던게..

strlen("...") 처럼 안전하고 11처럼 빠른 코드를 원하셨던게아닌가요?

sizeof("...") 를 사용하시면 될 것 같습니다.

antibug의 이미지

컴파일타임에 고정된 문자열이라면

char strMsg[] = "Hello, World." ;
memcmp( buf, strMsg, sizeof(strMsg) ) ;

정도가 좋을것 같군요. strMsg를 포인터로 하는 치명적인 실수만 없다면요... ^^;

근데 strMsg가 컴파일타임에 고정된 것이 아니라면 strcmp나 비슷하게 만드는
수밖에 없을 듯한데요...

char *input = ... ;
char *compare = ... ; // "Hello, World."?
while( *compare ) if ( *input ++ != *compare ++ ) return false ;
return true ;

뭐 이정도로 만들면 0으로 끝나는 문자열 검사는 될거 같네요...
memcmp를 쓴걸 보면 문자열 검사도 아닌거 같긴 한데 그럴 경우엔 음...
별로 방법이 없어보이네요. 길이를 어디선가 구해야 되니까... 길이를 가지고
있는 버퍼를 사용하거나 뭐 그래야 될것 같군요.

--------------------------------------
재미없는 일은 하지 말자는 인간 쓰레기.
-.-;

terra의 이미지

아 답변주신 분들 모두 대단히 감사 드립니다..
어제 글을 올렸는데 이렇게나 답이 많이 올라왔네요.. 정말 감사 드립니다.

주요한 논쟁은 strcmp, memcmp 의 문제 였는데요..
제 프로그램의 경우, 한쪽 비교 대상이 문자열 상수로 한정되어 있었기에 고정길이의 memcmp를 사용하였습니다. 이부분은 괜찮은것 같구요..

sizeof()를 고려해 보았으나.. 지금 테스트를 해보니

sizeof("hello world") = 12  //   \0을 포함해서 계산이 되는군요.. 
strlen("hello world") = 11 

최종 선택은 이 방법이 가장 안전하고 빠를것 같습니다..

#define STR1 "hello world" 
#define STR1_LEN 11 
memcmp(buf, STR1, STR1_LEN); 

답글 주신 모든 분들께 정말 감사 드립니다.

macgebi의 이미지

sizeof(...) + 1 하시면 되잖아요.. ^^

charsyam의 이미지

macgebi wrote:
sizeof(...) + 1 하시면 되잖아요.. ^^

저도 개인적으로는 sizeof(...) - 1이 낳을듯... ^^ 그럼 고운 하루

=========================
CharSyam ^^ --- 고운 하루
=========================

익명 사용자의 이미지

찾아보니 glibc 2.3.1 - i686 에 최적화 되면 strcmp 및 memcmp는
다음의 코드를 가지게 되는군요.

굉장히 철학적이고도 작고 빠르고! 딱 제가 추구하는 코드네요.

결국 memcmp가 약간의 속도 우월성을 보이는 것은 당연하고요.

참고로 memcmp는 i386이후 별로 최적화할 건더기가 없군요.

/* Highly optimized version for ix86, x>=6.
   Copyright (C) 1999, 2000, 2002 Free Software Foundation, Inc.
   This file is part of the GNU C Library.
   Contributed by Ulrich Drepper <drepper@cygnus.com>, 1999.

   The GNU C Library is free software; you can redistribute it and/or
   modify it under the terms of the GNU Lesser General Public
   License as published by the Free Software Foundation; either
   version 2.1 of the License, or (at your option) any later version.

   The GNU C Library is distributed in the hope that it will be useful,
   but WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
   Lesser General Public License for more details.

   You should have received a copy of the GNU Lesser General Public
   License along with the GNU C Library; if not, write to the Free
   Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
   02111-1307 USA.  */

#include <sysdep.h>
#include "asm-syntax.h"
#include "bp-sym.h"
#include "bp-asm.h"

#define PARMS	LINKAGE		/* no space for saved regs */
#define STR1	PARMS
#define STR2	STR1+PTR_SIZE

        .text
ENTRY (BP_SYM (strcmp))
	ENTER

	movl	STR1(%esp), %ecx
	movl	STR2(%esp), %edx
	CHECK_BOUNDS_LOW (%ecx, STR1(%esp))
	CHECK_BOUNDS_LOW (%edx, STR2(%esp))

L(oop):	movb	(%ecx), %al
	cmpb	(%edx), %al
	jne	L(neq)
	incl	%ecx
	incl	%edx
	testb	%al, %al
	jnz	L(oop)

	xorl	%eax, %eax
	/* when strings are equal, pointers rest one beyond
	   the end of the NUL terminators.  */
	CHECK_BOUNDS_HIGH (%ecx, STR1(%esp), jbe)
	CHECK_BOUNDS_HIGH (%edx, STR2(%esp), jbe)
	LEAVE
	ret

#ifndef __BOUNDED_POINTERS__
L(neq):	movl	$1, %eax
	movl	$-1, %ecx
	cmovbl	%ecx, %eax
#else
L(neq):	movl	$1, %eax
	ja	L(chk)
	negl	%eax
	/* When strings differ, pointers rest on
	   the unequal characters.  */
L(chk):	CHECK_BOUNDS_HIGH (%ecx, STR1(%esp), jb)
	CHECK_BOUNDS_HIGH (%edx, STR2(%esp), jb)
#endif

	LEAVE
	ret
END (BP_SYM (strcmp))


/* Compare two memory blocks for differences in the first COUNT bytes.
   Copyright (C) 1995, 1996, 1997, 2000 Free Software Foundation, Inc.
   This file is part of the GNU C Library.

   The GNU C Library is free software; you can redistribute it and/or
   modify it under the terms of the GNU Lesser General Public
   License as published by the Free Software Foundation; either
   version 2.1 of the License, or (at your option) any later version.

   The GNU C Library is distributed in the hope that it will be useful,
   but WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
   Lesser General Public License for more details.

   You should have received a copy of the GNU Lesser General Public
   License along with the GNU C Library; if not, write to the Free
   Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
   02111-1307 USA.  */

#include <sysdep.h>
#include "asm-syntax.h"
#include "bp-sym.h"
#include "bp-asm.h"

#define PARMS	LINKAGE+4	/* space for 1 saved reg */
#define BLK1	PARMS
#define BLK2	BLK1+PTR_SIZE
#define LEN	BLK2+PTR_SIZE

	.text
ENTRY (BP_SYM (memcmp))
	ENTER

	pushl %esi		/* Save callee-safe registers.  */
	movl %edi, %edx		/* Note that %edx is not used and can
				   so be used to save %edi.  It's faster.  */

	movl BLK1(%esp), %esi
	movl BLK2(%esp), %edi
	movl LEN(%esp), %ecx
	CHECK_BOUNDS_LOW (%esi, BLK1(%esp))
	CHECK_BOUNDS_LOW (%edi, BLK2(%esp))

	cld			/* Set direction of comparison.  */

	xorl %eax, %eax		/* Default result.  */

	repe			/* Compare at most %ecx bytes.  */
	cmpsb
	jz L(1)			/* If even last byte was equal we return 0.  */

	/* The memory blocks are not equal.  So result of the last
	   subtraction is present in the carry flag.  It is set when
	   the byte in block #2 is bigger.  In this case we have to
	   return -1 (=0xffffffff), else 1.  */
	sbbl %eax, %eax		/* This is tricky.  %eax == 0 and carry is set
				   or not depending on last subtraction.  */

	/* At this point %eax == 0, if the byte of block #1 was bigger, and
	   0xffffffff if the last byte of block #2 was bigger.  The latter
	   case is already correct but the former needs a little adjustment.
	   Note that the following operation does not change 0xffffffff.  */
	orb $1, %al		/* Change 0 to 1.  */

L(1):	CHECK_BOUNDS_HIGH (%esi, BLK1(%esp), jbe)
	CHECK_BOUNDS_HIGH (%edi, BLK2(%esp), jbe)
	popl %esi		/* Restore registers.  */
	movl %edx, %edi

	LEAVE
	ret
END (BP_SYM (memcmp))

#undef bcmp
weak_alias (BP_SYM (memcmp), BP_SYM (bcmp))

댓글 달기

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
이것은 자동으로 스팸을 올리는 것을 막기 위해서 제공됩니다.