예전 모 회사 프로그래머가 신의 한수를 알려주겠다고 memcpy를 한번 짜보라고 하시더군요..

그래서 그냥 생각나는데요 조금 노멀하게...

 

void MyMemcpy(void *dest, void *src, size_t len){

char *dest8 = (char*)&dest;

char *src8 = (char*)&src;

 

while(--len){

*dest8++ = *src8++;

}

}

 

했는데, 이거 봐서 님의 경력은 얼마 안되네요 ㅋㅋ 하시더군요

ㅡ_ㅡ...

 

그래서 조금 고친것이

dest메모리 영역과 src메모리 영역이 겹치는지 확인하는 코드를 넣었습니다.

bool MyMemCpy(void *dest, void *src, size_t len)
{
    char *dest8 = (char*)dest;
    char *src8 = (char*)src;

    /////////////////////////////////
    //dest ■■■■■              //1.dest 메모리 영역안에 src가 있거나
    //        src □□□□□
    //            dest ■■■■■  //2.src 메모리 영역안에 dest가 있으면 안됨
    /////////////////////////////////

    if((dest8 <= src8 //1검사
       && src8 <= dest8+len)
    ||(src8 <= dest8 //2검사
       && dest8 <= src8+len))
    {
        return false;
    }

    while(len--){
        *dest8++ = *src8++;
    }

    return true;
}

 

뭐 여기까지 하면 그래도 안전하지 않냐라는 생각이 들었습니다만...

 

최근 boost 라이브러리(mpl 프로그래밍)쪽을 공부하면서 이런 방법이 나오더군요..

#include <typeinfo>
#include <algorithm>
#include <iterator>
#include <memory>

#include <boost/test/included/prg_exec_monitor.hpp>
#include <boost/timer.hpp>
#include <boost/type_traits.hpp>

 

//기존의 copy와 차별화를 위한 네임스페이스
namespace opt{
    namespace detail{
        //이건 직접 대입시켜서 복사하는 방식
        template<typename l1, typename l2, bool b>
        l2 copy_imp(l1 first, l1 last, l2 out, const boost::integral_constant<bool, b>&)
        {
           while(first != last){
                *out = *first;
                ++out;
                ++first;
            }
            return out;
        }

 

       //memcpy를 이용하는 방식
       template<typename T>
       T* copy_imp(const T *first, const T *last, const boost::true_type&)
       {
            memcpy(out, first, (last-first)*sizeof(T));
            return out+(last-first);
        }
    }

 

    //실제 copy명령어
    template <typename l1, typename l2>
    inline l2 copy(l1 first, l1 last, l2 out)
    {
          //복사하는것의 타입을 알아내서
          typedef typename std::iterator_traits<l1>::value_type value_type;

 

    //l1에대해서 "C& C::operator=(const C&);" 와 같은 정의가 없다면
    //trivial_assignment operator 를 가지게 됨. 

    //하지만 컴파일러 능력상 이걸 알아내는건 제각각임;;;
    return detail::copy_imp(first, last, out, boost::has_trivial_assign<value_type>());

    }

}

 

/////////////////////////////////////////////////////////////////////////////////////////////////////

// 이 밑은 위의 소스를 테스트 하는 소스

const int array_size = 1000;
int   i_array_[array_size] = {0,};
const int ci_array_[array_size] = {0,};
char  c_array_[array_size] = {0,};
const char cc_array_[array_size] = {0,};

int *i_array   = i_array_;
const int *ci_array  = ci_array_;
char *c_array   = c_array_;
const char *cc_array = cc_array_;

const int iter_count = 1000000;

 

using namespace std;

int cpp_main(int argc, char* argv[])
{
    boost::timer t;
    double result;
 
    cout << "Measuring times in micro-seconds per "<< array_size << " elements processed" << endl;
    cout << "Testing copy... " << endl;
    cout << "  [some standard liberay version may already perform this optimisation.]" << endl;
  
    //////////////////////////////////////////////////////////////////////////
    //cache load
    opt::copy(ci_array, ci_array + array_size, i_array);

    //time optimised verstion
    t.restart();
    for(int i=0; i< iter_count; ++i){
        opt::copy(ci_array, ci_array + array_size, i_array);
    }
    result = t.elapsed();
    cout << "opt::copy<const int*, int*> : " << result << endl;

 

    //////////////////////////////////////////////////////////////////////////
    //cache load;
    std::copy(ci_array, ci_array + array_size, i_array);

    //time standard version
    t.restart();
    for(int i=0; i< iter_count; ++i){
        std::copy(ci_array, ci_array + array_size, i_array);
    }
    result = t.elapsed();
    cout << "std::copy<const int*, int*> : " << result << endl;

  

    //////////////////////////////////////////////////////////////////////////
    //cache load
    opt::copy(cc_array, cc_array + array_size, c_array);

    //time optimised verstion
    t.restart();
    for(int i=0; i< iter_count; ++i){
        opt::copy(cc_array, cc_array + array_size, c_array);
     }
     result = t.elapsed();
     cout << "opt::copy<const char*, char*> : " << result << endl;

 

     //////////////////////////////////////////////////////////////////////////
     //cache load;
     std::copy(cc_array, cc_array + array_size, c_array);

     //time standard version
     t.restart();
     for(int i=0; i< iter_count; ++i){
         std::copy(cc_array, cc_array + array_size, c_array);
     }
     result = t.elapsed();
     cout << "std::copy<const char*, char*> : " << result << endl;

  

     return 0;
}

 

메타프로그래밍은 프로그램 소스를 프로그래밍 하는 개념인데...

위의 메모리 복사 예제 처럼 C++의 템플릿을 가지고, 컴파일러가 타입에 따라 가장 적절한 알고리즘을 선택 할 수 있게 하거나

미리 상수 같은걸 프로그램 실행 시점에서 결정하지 않고 컴파일 시점에서 결정하게 하여

더 빠른 프로그램 속도를 갖게 하는 비법? 꼼수? 학문? 인거 같습니다.

 

진정한 신의 한수... orz....

 

문제는 내가 아직 이걸 프로젝트에 써먹으면서 머리로 이해 + 손으로 익어야 하는데.......

이 개념 자체가 난해하고 어려워서 자료도 그다지 많이 없고

그나마 boost로 익히는 메타프로그래밍 이라는 책을 사서 봤지만....

 

미국 책 스타일... orz...

그냥 개념만 쫙 ~~~ 설명됬고... 제가 멍청해서 그런지 개념 잡기가 너무 난해 하네요..

영어로 표현하면 보는 내내 so what? 이라는 느낌밖에 안왔습니다. ㅠㅠ

 

어쨋든.. 요즘 boost 쪽... 꽤 유용하고 재미있는 프로그래밍 방법론이라 생각하네요..

 

 서버 전반적인 복습을 하고 있습니다만...

DB를 붙이려고 하니

 

공급자를 찾을 수 없다는 에러가 나왔습니다.

 

 

평소 회사에는 없던일이라 조금 당황하고 이것저것 삽질했습니다만..

결국위에 노란 형광펜으로 그은 SQLNCLI 가 문제였습니다.

 

이게 MS-SQL 2005, 2008 과 접속할때는 SQLNCLI 이지만,

제 집에 있는 PC에는 MS-SQL2012Express라, 이럴경우에는 SQLNCLI10 으로 지정해야 한다는군요.

 

 

 

.... 음.... orz

 

#include <iostream>

using namespace std;

 

//간단한 텍스트 뒤집기
void recive(char s[])
{
    for(int i=0, j=strlen(s)-1; i<j; ++i, --j){
        char t = s[i];
        s[i] = s[j];
        s[j] = t;
    }
}

 

//안전한 메모리 복사

//차라리 memcpy_s 사용할것 이건 안에 어셈이라 더 빠름
bool MyMemCpy(void *dest, void *src, size_t len)
{
     char *des8 = (char*)dest;
     char *src8 = (char*)src;

     /////////////////////////////////
     //dest ■■■■■               //1.dest 메모리 영역안에 src가 있거나
     //      src □□□□□
     //            dest ■■■■■   //2.src 메모리 영역안에 dest가 있으면 안됨
     /////////////////////////////////
 
     if((des8 <= src8    //1검사
                && src8 <= dest8+len)
     ||(src8 <= des8    //2검사
                && des8 <= src8+len))
     {
            return false;
      }

   

      while(len--){
            *des8++ = *src8++;
      }

      return true;
}

 

//////////////////////////////////////////////

int main()
{
      char s[5] = {'a','b','c','d', '\0'};
      char d[5];

      for(int i=0; i<strlen(s); ++i){
          cout << s[i] << ',';
      }

     cout << endl;
 

     recive(s);

     for(int i=0; i<strlen(s); ++i){
         cout << s[i] << ',';
     }
     cout << endl;
 
     if(MyMemCpy(&s[3], &s[0], _countof(d))){
         for(int i=0; i<strlen(d); ++i){
            cout << d[i] << ',';
         }
     }
     return 0;
}

 

 

 

 

 

'C/C++언어' 카테고리의 다른 글

printf의 흔하진 않지만 때론 굉장히 필요한 포맷들  (0) 2013.10.23
memcpy에 대한 고찰...  (0) 2012.11.27
[ VC11-C++11 ] range base for - 1  (0) 2012.09.24
코드 최적화 팁  (0) 2012.09.10
CAtlMap 사용법 정리  (0) 2012.08.25

출처 : http://vsts2010.net/712

 

VC10에서 선보였던C++11의 기능 중 강력하면서 사용하기 쉽고, 자주 사용한 기능이 아마 'auto'이지 않을까 생각합니다. 예전에 강연을 할 때 auto와 관련된 예제를 보여드리면 많은 분들이 아주 좋아하시더군요(좀 놀라기도 하시더군요^^). 어떤 분들은 딴 건 제쳐두고 이것 때문이라도 VC10을 사용해야겠다는 분들이 있었습니다.

이번 VC11에서도'auto'와 같은 강력한 기능이 있습니다. 바로 'range base for' 입니다. 이것을 사용하면 반복문을 아주 쉽고, 강력하게 사용할 수 있습니다.

VC 특화 기능인 for each와 비슷하기 때문에 기존에 for each를 사용하고 있다면 이제는 range base for로 쉽게 바꾸어서 사용하면 됩니다.

예제를 통해 일반적인 for , VC for each,range base for문의 차이를 예제를 통해서 보겠습니다.

< 예제. 1 >

#include <iostream>

int main()

{

int NumberList[5] = { 1, 2, 3, 4, 5 };

std::cout<< "일반적인 for "<< std::endl;

for( int i = 0; i < 5; ++i )

{

std::cout<< i << std::endl;

}

std::cout<< "VC++ 특화의 for each" << std::endl;

for each( int i in NumberList )

{

std::cout<< i << std::endl;

}

std::cout<< "range base for " <<std::endl;

for( auto i : NumberList )

{

std::cout<< i << std::endl;

}

return 0;

}

< 실행 결과 >


<예제.1>을 보면 일반적인 for 문은

for( int i = 0; i < 5; ++i )

와 같이 시작과 종료 조건, 증가 값 이렇게 3개의 조건에 의해서 반복 됩니다.

그러나 range base for문은 VC만의 반복문인 for each와 비슷하게 데이터셋 변수와 이 데이터셋 요소의 타입을 선언하면 됩니다.

for( auto i : NumberList )

기존의 for 문에 비해서 또는 for each 보다도 간편해졌고, for each는 표준이 아닌 VC만의 기능인 것에 비해서 range base for C++ 표준 기능입니다.

range base for 문의 문법은 아래와 같습니다.

for ( for-range-declaration : expression ) statement

range base for 덕분에 반복문의 사용이 쉬워졌고, for 문을 사용할 때 종료 조건이 잘못되어 메모리 침범을 하는 위험도 피할 수 있게 되었습니다.

'C/C++언어' 카테고리의 다른 글

memcpy에 대한 고찰...  (0) 2012.11.27
간단한 텍스트 뒤집기, 메모리 복사 함수  (0) 2012.10.19
코드 최적화 팁  (0) 2012.09.10
CAtlMap 사용법 정리  (0) 2012.08.25
MSXML 파싱  (0) 2012.07.03

출처 : http://blog.naver.com/chaewh83/140016180847

 

이걸 내가 쓰는 프로그래밍으로 풀면

 

1. 컴파일러 최적화 옵션에서 '속도 최적화' 대신 '코드 크기 최적화' 설정이 캐쉬 성능을 향상시켜 좀 더 빠른 코드를 얻게 함.

 

-> 네트워크 프로그래밍의 경우, 경우에 따라 덜 최적화된 코드가 필요하므로 volatile 를 써야 함.

 

2. 각 CPU(인텔의 SSE, SSE2 / AMD 3D Now)의 SIMD를 활용하는 함수를 만들것.

-> 서버는 현재로선 인텔 CPU니까.....

 

3. 가능한 나눗셈 연산을 최소화 할것.

예)

-> b = a / m;

    c = d / m;

이 있으면

 

-> m = 1/m;

    b = a * m;

    c = d * m;

... 쉬프트가 여러우면 곱셈으로 해버림...

 

4. switch문을 쓸때 연속된 수치를 사용할것, 그러면 VC가 테이블 형태의 분기를 만들어줌.

-> 즉 가능하면 enum {} 으로 쓰는게 좋다는 이야기임.

 

예) case 0:
     case 1:

     case 2: ... (역순으로도 상관없음)

 

잘못) case 2:

        case 4:

        case 3:

 

5. 펜티엄 pro 이상에서는 CMOVxx/FCMOVxx 같은 조건적 이동 명령이 가능.

간단한 if문보다는 ? 를 사용할것.

 

->A == 0 ? choice1 : choice 2;

 

다만 디버깅할때 어느쪽 분기로 떨어지는지는 추적하기 힘드므로

#if _DEBUG

 if()...

#else

 ?...

#endif

를 쓰는것도 나쁘지 않을듯.

 

6. 자주 사용하는 자료 구조는 32bit 배수로 정렬시켜서 사용할것,

컴퓨터의 캐쉬라인을 최적으로 쓸 수 있기때문에 성능이 크게 향상됨. 팬티엄4는 L1캐쉬가 64바이트고 L2가 128바이트

이런걸 감안해서 쓰는 기법을 padding 이라 함.

 

7. sin, cos, tan, exp, arcsin등 수학 함수는 가급적 쓰지말고,

각 수치 데이터를 테이블로 만들어서 간략한 함수로 만들어 쓸것

 

-> psp나 3ds때도 이렇게 했는데...

 

8. 부동소수점은 가급적 double보다 float를 사용할것.

double 의 나눗셈은 39사이클이 걸리지만, float는 19사이클임. (단 2의 제곱수로 나누면 8사이클정도만 걸림)

그리고 float형일때 반드시 뒤에 f를 붙일것.

float a = 1.0; 보다는

float a = 1.0f; 이쪽이 더 빠름

 

9. 순서에 상관이 없다면 후연산자 보다는 선연산자를 사용할것.

a++; 보다는 ++a; 를 쓸것.

 

->출처쪽에선 이유가 안나와 있지만, 이걸 어셈 코드상에서 보면

a++; => a = a + 1; 개념이고, ++a; => a+1; 개념임.

 

10. 가능한 const를 사용할것. 컴파일러가 좀더 최적화를 잘 해줄 가능성이 높아짐.

 

11. 메모리 관리 함수는 따로 사용하는것이 좋음. malloc 과 free는 느리기 때문에 가능한 메모리풀을 만들어사용할것.

 

'C/C++언어' 카테고리의 다른 글

간단한 텍스트 뒤집기, 메모리 복사 함수  (0) 2012.10.19
[ VC11-C++11 ] range base for - 1  (0) 2012.09.24
CAtlMap 사용법 정리  (0) 2012.08.25
MSXML 파싱  (0) 2012.07.03
프로그램 시간 측정  (0) 2012.03.21

+ Recent posts