[입 개발]GIT: 여러 개의 commit 하나로 합치기

솔직하게 말하자면, 저는 Git을 잘 사용하지 못합니다. 그냥 아주 간단한 수준에서
1] branch 생성
2] branch merge
3] git pull
4] git push

여기다가 github을 이용해서 pull request 보내는 정도가 저의 전부입니다.

그런데, 가끔식 보면, git을 이용해서 pull request를 보내면 여기에 수정해야할 때가 생깁니다.
이럴 경우 해당 branch에 그냥 계속 commit 하면 자동으로 pull request에 commit이 쌓이게 됩니다.

커미터가, 이런것에 신경을 안쓰고 그냥 커밋할 경우에는 전혀 문제가 없지만(부끄러운 기록이 다 남게 됩니다)
자신이 이 일련의 패치들을 하나로 묶어서 보내고 싶을 때, 또는 커미터가 이거 하나로 묶어주세요 라고 요구할때가
생깁니다. 전 그럴때는 -_- 지금까지 새로운 브랜치를 만들어서 해당 패치들을 옮겨서 하나로 만들어서 제출했습니다.
(아 부끄러워요.)

git 의 기능중에 rebase 라는게 있습니다. 저도 자세히는 잘 모릅니다. 저는 제가 딱 필요한 만큼만 공부를 해둬서 쿨럭…
이 git rebase 를 이용하면 여러 개의 커밋을 하나로 합칠 수 가 있습니다.

아래와 같은 명령으로 4개의 commit 정보를 볼 수 있습니다.

git rebase -i HEAD~4

$ git rebase -i HEAD~4

pick 01d1124 Adding license
pick 6340aaa Moving license into its own file
pick ebfd367 Jekyll has become self-aware.
pick 30e0ccb Changed the tagline in the binary, too.

# Rebase 60709da..30e0ccb onto 60709da
#
# Commands:
#  p, pick = use commit
#  e, edit = use commit, but stop for amending
#  s, squash = use commit, but meld into previous commit
#
# If you remove a line here THAT COMMIT WILL BE LOST.
# However, if you remove everything, the rebase will be aborted.
#

여기서 pick 이라고 되어 있는 부분을 다음과 같이 squash 바꿔주면 하나의 커밋으로 변경할 수 있습니다.

pick 01d1124 Adding license
squash 6340aaa Moving license into its own file
squash ebfd367 Jekyll has become self-aware.
squash 30e0ccb Changed the tagline in the binary, too.

그럼 이제 하나의 커밋이 되었는데 이것을 자신의 브랜치로 push 하면 다음과 같은 오류가 발생합니다.

charsyam@ubuntu:~/projects/redis-resync/src$ git push origin unexpected_resync
Username for 'https://github.com': charsyam
Password for 'https://charsyam@github.com': 
To https://github.com/charsyam/redis.git
 ! [rejected]        unexpected_resync -> unexpected_resync (non-fast-forward)
error: failed to push some refs to 'https://github.com/charsyam/redis.git'
To prevent you from losing history, non-fast-forward updates were rejected
Merge the remote changes (e.g. 'git pull') before pushing again.  See the
'Note about fast-forwards' section of 'git push --help' for details.

현재 git의 history 정보를 잃어버리게 되기 때문에 reject 를 한다는 것인데, 제가 원하는 것은
이걸 강제로 push 시키는 겁니다. 이 때 브랜치명에 ‘+’를 포함해주면 강제로 push가 되게 됩니다.

[souce language=’c’]
charsyam@ubuntu:~/projects/redis-resync/src$ git push origin +unexpected_resync
Username for ‘https://github.com’: charsyam
Password for ‘https://charsyam@github.com’:
To https://github.com/charsyam/redis.git
+ 2d3ecb5…19d8783 unexpected_resync -> unexpected_resync (forced update)
[/source]

이렇게 하면 pull request에도 이것이 하나로 합쳐져서 보여지게 됩니다.

[입 개발] 알면 좋고, 몰라도 상관 없는 gcc 옵션과 linux version 정보

한 두달, 블로그를 쉬었습니다. 사실 쉴려고 한 건 아니고, 번역을 하다 만것들도 꽤 되고, 영어가 점점 더 부족해져가기도 하구요. 캐나다에 오고나서 도리어 필리핀 보다 영어공부하는 시간이 줄다 보니, 이것 저것 블로그도 점점 죽어가고 있습니다. 최근에 영어 공부보다 개발 공부를 더 하다보니, 몇가지 알면 좋고 몰라도 상관없는 정보들을 많이 발견하게 되었습니다.(정정합니다. 조금)

 

1. Linux Kernel 버전 정보나 GLIBC 버전 정보를 어떻게 알아야 할까?

– /usr/include/linux/version.h 에 보면 LINUX_VERSION_CODE 라는 것이 있습니다. 이걸 이용하면 현재 커널 버전을 쉽게 알 수 있습니다.  다만 이때 주의할 것은, 해당 값이 0x020612 이런식으로 되어있고 앞에 1byte가 major, 뒤의 1byte가 minor, 마지막 1byte 가 patch 순서인데 예를 들어 리눅스 커널 2.6.18 이면 0x020612 가 되게 됩니다. 마지막에 12는 hex 코드로 18 -> 12로 매칭되는 겁니다. 이거 실수하시면 많이 틀릴 수 있습니다.

 

2. GLIBC 버전은 어떻게 알까?

– 처음에는 다음과 같은 함수를 이용했습니다.

<pre>#include <stdio.h>
#include <gnu/libc-version.h>

int main()
{
	/* Or we could just run /lib/libc.so.6.  */

	puts(gnu_get_libc_version());
	return 0;
}

그런데 /usr/include/features.h 에 보면 GLIBC 버전에 대한 정의가 있습니다. 다음과 같습니다.

</pre>
#define __GLIBC__ 2
#define __GLIBC_MINOR__ 15

#define __GLIBC_PREREQ(maj, min) \

((__GLIBC__ << 16) + __GLIBC_MINOR__ >= ((maj) << 16) + (min))

 

정말 이거 몰라서 한참을 고민했었네요.

 

3. gcc option -march=native

– gcc 가 CPU에 맞추어서 코드를 만들어주는 것을 다들 아실껍니다. 특별한 옵션을 안주시고 32bit에서 빌드하면 그냥 i386 instruction에 맞춰서 빌드가 되는데, 이럴 경우 “undefined reference to__sync_add_and_fetch_4′” 라는 오류를 만나게 됩니다. i386에 다음과 같은 명령셋이 없기 때문인데요. 이 때 -march=i686 등으로 현재의 CPU 버전을 적어주면 잘 빌드되게 됩니다. 그런데 뒤에 계속 새로운 CPU는 나오고 같은 다시 지원해주지 않는 이슈가 뒤에 발생할 수도 있는데, GCC 4.2 부터 -march=native 라는 옵션이 생겼습니다. 이건 뭐냐 하면 현재 해당 장비에서 쓰는 CPU 타입에 맞춰주겠다는 뜻입니다. 다만 크로스 컴파일이나 이런거 쓰시는 분들은 조심하셔야겠죠? target 이 아니라 host에 최적화될테니 ^^

Python Development With Boost.Python

Python 을 사용하다보면, 그 편리함에 감탄하다가도, 좀 더 나은 성능을 가지고 싶다라는 욕망에 몸을 던지게 됩니다. 결국 가끔씩 속도의 유혹에 이기지 못해, Binding 이라는 외도를 하게 되는데……

이 외도를 하는 방법에 3가지가 있다고 합니다.
1. Pure Python Binding
2. Boost.Python
3. SWIG

여기서 제가 해본 것은 1,2번입니다. 1,2번은 실제로 성능 차이는 거의 없는 것 같습니다.

-----Boost.Python Binding----
4 function calls in 1.232 seconds
Ordered by: standard name

ncalls tottime percall cumtime percall filename:lineno(function)
1 0.000 0.000 1.232 1.232 :1()
1 1.190 1.190 1.232 1.232 test.py:4(testgo)
1 0.000 0.000 0.000 0.000 {method 'disable' of '_lsprof.Profiler' objects}
1 0.042 0.042 0.042 0.042 {range}

----Pure Python Binding ----
1000004 function calls in 1.266 seconds

Ordered by: standard name

ncalls tottime percall cumtime percall filename:lineno(function)
1 0.000 0.000 1.266 1.266 :1()
1 0.509 0.509 1.266 1.266 test.py:4(testgo)
1 0.000 0.000 0.000 0.000 {method 'disable' of '_lsprof.Profiler' objects}
1000000 0.715 0.000 0.715 0.000 {pure_test.pure_test}
1 0.042 0.042 0.042 0.042 {range}

단지 이상한 것은 Pure Python Binding 에서는 cProfile이 제대로된 호출을 찾아내는데, Boost.Python 에서는 제대로 된 Profile 결과가 나오지 않습니다. 다만, 사용시간은 제대로 나옵니다.

그런데, Pure Python Binding 은 개발이 조금 귀찮습니다. Boost.Python 은 Boost를 설치해야 합니다. 설치시에도 –with-python 옵션을 줘야만 함께 생성됩니다. 그 대신 제공해주는 것이 풍부합니다. 그래서 간단한 Boost.Python 모듈 소스를 소개합니다.

먼저 개발을 위해서는 다음과 같은 setup.py를 만들어줘야 합니다.

from distutils.core import setup, Extension

module1 = Extension('boost_test',
                     include_dirs = ['/boost/include'],
                     libraries = ['boost_python'],
                     library_dirs = ['/boost/lib'],
                     sources = ['src/boost_test.cpp'])

setup (name = 'boost_test',
       version = '1.0',
       description = 'This is a demo package',
       ext_modules = [module1])

그리고 소스는 굉장히 간단합니다. 실용성을 위해서 list 안에 list 가 있는 케이스로
작성을 했습니다. list 나 tuple 이나 사용법은 동일합니다.

#include 
#include 
#include 

using namespace boost::python;

int get_int( tuple list1, list listoflist ){
    return boost::python::len(list1);
}

const char *get_string( list list1, list listoflist ){
    list mylist = boost::python::extract(listoflist[1]);
    //str 을 쓰면 object 를 string으로 만들어 줍니다.
    //str 을 안쓰고 다른 type 이 오면 TypeConvertError 가 생깁니다.
    std::string k = boost::python::extract(str(mylist[0]));
    return k.c_str();
}

BOOST_PYTHON_MODULE(boost_test)
{
    def("get_string", &get_string );
    def("get_int", &get_int );
}

마지막으로 간단한 테스트 코드입니다.

import boost_test

if __name__=="__main__":
    print boost_test.get_int( ('1','2','3'), ['2'] )
    print boost_test.get_string( ['1','2','3'], [['2'],['1','3']] )

class 를 binding 하는 방법 역시 boost.python 에서는 굉장히 쉽고, 이미 boost 페이지 내에서 찾기 쉽기 때문에 생략합니다.

Memcached VS DBMS Performance Comparison

이제는 대부분의 기업들에서 사용하고 있지만, 아직도 모르시는 분들을 위해서 간단하게 정리합니다.

혹시 조금의 비용만 들이고, 당신의 웹사이트나 어플맄케이션의 성능을 10배 이상 올릴 수 있다면

어떻게 하시겠습니까?

가장 손쉬운 방법은 바로 DBMS와 함께 Memcached 를 사용하는 것입니다.

보통 일반적으로 2 Tier , App -> DBMS 이런형태로 사용하게 되는데, 여기에 중간에 Memcached를

집어넣으므로써 굉장히 손쉽게 성능을 향상시킬 수 있습니다.

일단 단순 DBMS Select 쿼리를 했을 때 걸리는 시간입니다.

1. select dbms
[time=0:00:00.035560]

2. get memcached
[time=0:00:00.000415]

입니다. 이것은 극단적인 예이긴 하지만, 거의 100배 정도 차이가 나는걸 볼 수 있습니다.

그렇다면, 어느 부분에 memcached 를 적용하는 것이 좋을까요?

1] Read 작업이 많은 경우!!!
2] 항상 같은 값이나, 일정 동안 같은 값을 가져가는 경우

즉, 단순 select 작업이 많은 경우에는 memcached를 이용하면 바로 위와 같은 성능 향상을
바로 획득할 수 있습니다.

여기서 memcached를 사용할 경우의 Tip 을 몇가지 소개합니다.
1. memcached 서버의 memory가 많을 수록 좋다.
-> 당연한 일이지만, memory 가 클 수록 더 많은 데이터를 Cache 할 수 있으므로 좋습니다.
-> O(1) 알고리즘이니, 데이터가 많아져도 속도의 저하가 없습니다.

2. memcached 서버와는 Connection Pool 등으로 Connection 작업을 줄인다.
-> 매번 작업할 때 마다, memcached 서버와 Connection 을 맺어야 하면, 배보다 배꼽이
-> 큰 경우가 발생합니다. 즉 한번 맺으면 쭈욱 계속 사용하는 것이 성능상 향상이 있습니다.

3. -f 옵션을 잘 사용하자.
-> memcached 는 slab allocator 를 사용하는 slab의 크기는 이 -f 옵션의 팩터를 이용해서ㅇ해서
-> 증가합니다. 즉 n 바이트 이하는 전부 n 이라는 chunk를 생성하게 됩니다. 그리고 이 n보다
-> 커지는 크기는 n*factor 크기 단위로 증가합니다. 즉 memory 를 적절히 사용하려면 이 -f를
-> 적절히 이용하면 효율적으로 사용할 수 있습니다.

python try-except Performance Comparison

Python 으로 코드를 짜면서 가장 많이 고민하게 되는 내가 정말 Python 을 Python 답게 짜고 있는지가 고민입니다. C/C++ 로만 코드를 작성했기 때문에(아직 C++도 C 처럼 짜고 있는데…) 여러가지가 고민을 가지게 되는데, 가장 강하게 고민이 드는 것 중에 하나가 예외 처리입니다.

c++에서도 try, catch 를 제공하지만, 잘 쓰지 않기 때문에 이런 류의 예외처리에 익숙하지 않은데, Python 에서는 모든게 try-except 로 처리해야 하는 것 같더군요.

익숙하지 않은 상황에서는 return value 와 try-except 를 혼용하는 이상한 구조가 되버렸습니다. 그래서 코드를 다시 정리를 하려고 생각하는데, C/C++ 만 이용하던 초급 개발자라서, try-except 를 쓰면 성능상에 제약이 생기지 않을까 라는 생각이 들어서 간단한 코드로 비교를 해보았습니다.

테스트는 time 을 이용해서 비교했고, 각각 1000000 번 시도한 결과 값을 측정했습니다. 일단 간단히 결과부터 보여드리자면 다음과 같습니다. 결과부터 말씀드리자면, try-except 를 써도 전혀 속도에 영향이 없다입니다. 반복 루프에서 100% 예외가 발생하는 상황이 아니라면, 거의 영향이 없었습니다. 저도 만족해서 써도 될듯 하네요 ^^ 그래서 all-try.py 만 속도가 많이 느립니다.

DaeMyung-Kang-ui-MacBook-Pro:try-except-performance charsyam$ time ./no-try.py

real 0m0.657s
user 0m0.603s
sys 0m0.050s
DaeMyung-Kang-ui-MacBook-Pro:try-except-performance charsyam$ time ./use-try-but-not-call.py

real 0m0.683s
user 0m0.627s
sys 0m0.051s
DaeMyung-Kang-ui-MacBook-Pro:try-except-performance charsyam$ time ./use-try.py

real 0m0.681s
user 0m0.625s
sys 0m0.052s
DaeMyung-Kang-ui-MacBook-Pro:try-except-performance charsyam$ time ./all-try.py

real 0m2.031s
user 0m1.975s
sys 0m0.052s

그럼 비교 코드를 올리겠습니다. 혹시나 뭔가 비교를 잘못한거 같다면 저에게 알려주시면 감사하겠습니다.

no-try.py

from test_config import TEST_CONFIG

list = []
def add_list( num ):
    list.append(num)
    if( num == TEST_CONFIG.TRYCOUNT ):
        return

for i in range( TEST_CONFIG.RANGE ):
    add_list(i)

use-try-but-not-call.py

from test_config import TEST_CONFIG

list = []
def add_list( num ):
    list.append(num)
    if( num == TEST_CONFIG.TRYCOUNT ):
        return

for i in range( TEST_CONFIG.RANGE ):
    try:
        add_list(i)
    except:
        print i

use-try.py

from test_config import TEST_CONFIG

list = []
def add_list( num ):
    list.append(num)
    if( num == TEST_CONFIG.TRYCOUNT ):
        raise Exception("Exception")

for i in range( TEST_CONFIG.RANGE ):
    try:
        add_list(i)
    except:
        continue

all-try.py

from test_config import TEST_CONFIG

list = []
def add_list( num ):
    list.append(num)
    raise Exception("Exception")

for i in range( TEST_CONFIG.RANGE ):
    try:
        add_list(i)
    except:
        continue

sqlite 의 새로운 대안 Berkeley DB

아이폰, 안드로이드폰에서 공통적으로 사용하는 DBMS 가 무엇이냐고 물으면, 조금이라도 아는 사람은

sqlite를 생각하게 됩니다. File 기반 DBMS 이고, 소스에 임베딩해서 사용할 수 있는, sql 문법까지 지원하는

DBMS는 그냥 sqlite 밖에 없었다고 생각하시면 됩니다.

그런데, 그에 대한 강력한 경쟁자가 떠올랐습니다. 그게 바로 Berkely DB입니다. -_- 이게 뭐야 하시는 분들이

바로 나오실 꺼 같은데, BDB는 예전부터 유명한 파일 기반 DB API였습니다. DBMS라고 부르지 않고 DB API로

부른 이유는 단순 Key Value Store 에 SQL 문법은 전혀 지원하지도 않았습니다.

 그런데 그런 BDB가 Oracle에 인수된 후… 그리고 Oracle Berkely DB 11g Release 2 라는 이름으로

2010년 8월 27일에 출시되었습니다. 벌써 5개월 정도 되었는데, 국내에는 그렇게 관련 정보가 없는듯 합니다.

그런데 이번에 SQL Interface 를 지원하고, ODBC, JDBC 등을 지원하는 최초의 버전이 나와버린겁니다. T.T

오오오 오라클을 찬양하라~~~

 거기다가 더더욱 엄청난 점은 -_- 기존에 sqlite3 로 빌드된 어플이라면 소스를 바꾸지 않고 라이브러리만

링크를 바꿔주면 빌드가 됩니다.( 이건 아예 상단부를 sqlite3 와 완전하게 동일하게 인터페이스를 설계했다고 합니다.

윗부분이 포팅된거일수도 있겠네요.) 그래서 dbsql 이라는 툴은 sqlite3 와 거의 동일합니다.

 실제로 libsqlite3 대신에 libdb_sql-5.1.a 를 링크해주자 -_- 그대로 돌아갑니다. 단 해당 기능을 사용하기 위해서는

빌드시에 sqlite3 헤더가 필요합니다.

그렇다면 이제 BDB와 sqlite3의 차이는 어떤점이 있을까요? BDB는 MVCC 라는 것을 지원합니다. Read 와 Write 의 경우에

Write 가 되더라도 Read를 정상적으로 할 수 있고, 그 반대도 할 수 있도록 처리해주는 기법입니다. 즉 동시에 여러개의

프로그램에서 BDB를 이용해서 insert, select, update 등을 행하더라도, 동시 처리 성능이 높아집니다. sqlite3의 경우에는

하나의 작업이 동작하면 다른 작업들은 전부 실패하게 됩니다. 단, 이런 기능으로 인해서 단일 스레드상의

단순 성능은 sqlite3가 높을듯 합니다. 해당 부분은 추후에 테스트 코드로 비교해보면 될듯 합니다.(같은 코드베이스에

빌드만 따로하면 되니 정확한 비교가 될듯 합니다. 뭐, 쿼리 실행 계획이 틀릴수도 있지만요 ) 그럼 BDB는 단점이 없느냐?

그런건 아닙니다. 만약 NFS 나 다른 프로토콜로 다른 서버에 있는 BDB 파일을 이용하려고 한다면, 위의 MVCC가 제대로

동작하지 않습니다. 현재 동기화 매커니즘이 하나의 머신안에서 여러 프로세스가 접근할 때 제어하는 형태로 구성되어

있기 때문입니다. 그러나 sqlite3의 경우는 File Lock 기반을 이용하므로, 여러 서버에서 동시에 접근하더라도, 성능은

떨어지지만, 제대로 된 동시성 제어가 되게 됩니다. BDB 쪽은 쫌 더 확인이 필요할듯 하네요.

 그래도 오늘 의외로 좋은 정보를 얻게 되어서 이렇게 정리해 둡니다.

 BDB 를 받으시면 안에 example의 sql 부분을 보시면 실제 sqlite3 함수를 그대로 쓰고 있다는 것을 알 수 있습니다.

pkg-config

 protobuf 를 설치하고 샘플을 빌드하는데 pkg-config 이 제대로 셋팅되지 않았다라는 메시지가

계속 나타났다 -_- 이거 왜 이럴까 T.T
 관심이 별로 없어서 신경을 안쓰던 것인데, 빌드할 때, 해당 라이브러리의 경로,
include 의 위치 빌드시 필요한 cflags, libs 등에 대한 정보를 미리 설정해둔것이다.
샘플은 다음과 같다.
prefix=/nadia
exec_prefix=${prefix}
libdir=${exec_prefix}/lib
includedir=${prefix}/include
Name: Protocol Buffers
Description: Google’s Data Interchange Format
Version: 2.3.0
Libs: -L${libdir} -lprotobuf  -pthread
Cflags: -I${includedir} -pthread
# Commented out because it crashes pkg-config *sigh*:
# Conflicts: protobuf-lite
이것의 장점은 빌드시에 실제 중요한 정보의 위치를 빌드 스크립트에서

    pkg-config --cflags protobuf         # print compiler flags
    pkg-config --libs protobuf           # print linker flags
    pkg-config --cflags --libs protobuf  # print both
이런 방법으로 얻어올 수 있다는 것이다. 빌드 시 실제 위치에 대한 정보를 알 필요없이
해당 정보만 셋팅해 주면 된다.