memory indexing differences

for 구문 사용시, 인덱싱하는 order 에 따라, 실행시간에 차이가 발생한다는 글을 보았다(참조: http://process3.blog.me/20030421397)

정말로 그럴까? 한번 확인해 보았다. 결론은? 확실한 차이가 있었다.

이유를 확인해보니, 메모리 캐시와 관련된 내용이었다.

 

— Program 1
#include <stdio.h>
#define MAX_CNT 100
int main(int argc, char** argv)
{
    int i, j, k;
    int test_data[MAX_CNT][MAX_CNT][MAX_CNT];
    for(i = 0; i < MAX_CNT; i++)
    {
        for(j = 0; j < MAX_CNT; j++)
        {
            for(k = 0; k < MAX_CNT; k++)
            {
                test_data[i][j][k] = 100;
            }
        }
    }
    return 0;
}
Result
$ time ./main
real    0m0.008s
user    0m0.004s
sys     0m0.004s
— Program 2
#include <stdio.h>
#define MAX_CNT 100
int main(int argc, char** argv)
{
    int i, j, k;
    int test_data[MAX_CNT][MAX_CNT][MAX_CNT];
    for(i = 0; i < MAX_CNT; i++)
    {
        for(j = 0; j < MAX_CNT; j++)
        {
            for(k = 0; k < MAX_CNT; k++)
            {
                test_data[k][j][i] = 100;
            }
        }
    }
    return 0;
}
$ time ./main
real    0m0.017s
user    0m0.013s

sys     0m0.004s

 

힙 영역 사용시, 보다 더 큰 차이를 확인할 수 있다.

— Program 1
#include <stdio.h>
#include <stdlib.h>
#define MAX_CNT 500
int main(int argc, char** argv)
{
    int i, j, k;
//    int test_data[MAX_CNT][MAX_CNT][MAX_CNT];
    int*** test_data;
    test_data = calloc(MAX_CNT, sizeof(int**));
    for(i = 0; i < MAX_CNT; i++)
    {
        test_data[i] = calloc(MAX_CNT, sizeof(int*));
        for(j = 0; j < MAX_CNT; j++)
        {
            test_data[i][j] = calloc(MAX_CNT, sizeof(int));
        }
    }
    for(i = 0; i < MAX_CNT; i++)
    {
        for(j = 0; j < MAX_CNT; j++)
        {
            for(k = 0; k < MAX_CNT; k++)
            {
                test_data[i][j][k] = 100;
            }
        }
    }
    return 0;
}
Result
$ time ./main
real    0m0.539s
user    0m0.367s
sys     0m0.172s
— Program 2
#include <stdio.h>
#include <stdlib.h>
#define MAX_CNT 500
int main(int argc, char** argv)
{
    int i, j, k;
//    int test_data[MAX_CNT][MAX_CNT][MAX_CNT];
    int*** test_data;
    test_data = calloc(MAX_CNT, sizeof(int**));
    for(i = 0; i < MAX_CNT; i++)
    {
        test_data[i] = calloc(MAX_CNT, sizeof(int*));
        for(j = 0; j < MAX_CNT; j++)
        {
            test_data[i][j] = calloc(MAX_CNT, sizeof(int));
        }
    }
    for(i = 0; i < MAX_CNT; i++)
    {
        for(j = 0; j < MAX_CNT; j++)
        {
            for(k = 0; k < MAX_CNT; k++)
            {
                test_data[k][j][i] = 100;
            }
        }
    }
    return 0;
}
Result
$ time ./main
real    0m5.344s
user    0m5.202s
sys     0m0.147s

Libevent event driven memory leak

진행중인 프로젝트에서 Libevent 를 사용중인데.. valgrind 메모리 누수 확인시 자꾸 메모리 누수가 발생하는 것을 확인했다.

어디가 문제일까… 기능상으로는 문제가 없었다.
malloc/calloc 할당한 부분도 문제가 없었다.
valgrind 가 지목한 문제 발생한 부분은 event_new() 부분이었다.

문제 발생 원인은 잘못된 libevent 사용에 있었다.
나는 다음과 같이 프로그램을 개발했었다.

/*
* main.c
*
* Copyright 2014 Sungtae Kim <pchero21@gmail.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
* MA 02110-1301, USA.
*
*
*/

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <event2/event.h>

typedef struct _wctx
{
struct event_base*  w_base;     // main base
} wctx;

static void cb_main_listen(__attribute__ ((__unused__)) int fd, __attribute__ ((__unused__)) short event, void *arg);
static void cb_process(__attribute__ ((__unused__)) int fd, __attribute__ ((__unused__)) short event, void *arg);
static void cb_process_detail(__attribute__ ((__unused__)) int fd, __attribute__ ((__unused__)) short event, void *arg);

int main(int argc, char **argv)
{

wctx* work = calloc(1, sizeof(wctx));

work->w_base = event_base_new();
struct event* main_listen = event_new(work->w_base, STDIN_FILENO, EV_READ, cb_main_listen, work);
event_add(main_listen, NULL);

event_base_dispatch(work->w_base);

printf(“System terminate…n”);
event_free(main_listen);
event_base_free(work->w_base);

return 0;
}

static void cb_main_listen(__attribute__ ((__unused__)) int fd, __attribute__ ((__unused__)) short event, void *arg)
{
wctx* work = (wctx*)arg;
struct event* w_event = evtimer_new(work->w_base, cb_process, work);

struct timeval  tVal;
tVal.tv_sec = 0;
tVal.tv_usec = 0;
evtimer_add(w_event, &tVal);
}

static void cb_process(__attribute__ ((__unused__)) int fd, __attribute__ ((__unused__)) short event, void *arg)
{
printf(“Process!!n”);
wctx* work = (wctx*)arg;
struct event* w_event = evtimer_new(work->w_base, cb_process_detail, work);

struct timeval  tVal;
tVal.tv_sec = 0;
tVal.tv_usec = 0;
evtimer_add(w_event, &tVal);

}

static void cb_process_detail(__attribute__ ((__unused__)) int fd, __attribute__ ((__unused__)) short event, void *arg)
{
printf(“Detail Process!!n”);
}

 

아래는 valgrind 로 메모리 누수를 확인한 내용이다.

pchero@MyGalaxy:~/workspace/Study/Program/LibEvent/memory_leak$ echo “Hello” | valgrind –leak-check=full ./main
==2222== Memcheck, a memory error detector
==2222== Copyright (C) 2002-2012, and GNU GPL’d, by Julian Seward et al.
==2222== Using Valgrind-3.8.1 and LibVEX; rerun with -h for copyright info
==2222== Command: ./main
==2222==
Process!!
Detail Process!!
System terminate…
==2222==
==2222== HEAP SUMMARY:
==2222==     in use at exit: 280 bytes in 3 blocks
==2222==   total heap usage: 12 allocs, 9 frees, 1,848 bytes allocated
==2222==
==2222== 136 bytes in 1 blocks are definitely lost in loss record 2 of 3
==2222==    at 0x4C2A2DB: malloc (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
==2222==    by 0x4E43A46: event_new (in /usr/lib/x86_64-linux-gnu/libevent-2.0.so.5.1.9)
==2222==    by 0x400A16: cb_process (main.c:73)
==2222==    by 0x4E41F93: event_base_loop (in /usr/lib/x86_64-linux-gnu/libevent-2.0.so.5.1.9)
==2222==    by 0x400933: main (main.c:49)
==2222==
==2222== 144 (136 direct, 8 indirect) bytes in 1 blocks are definitely lost in loss record 3 of 3
==2222==    at 0x4C2A2DB: malloc (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
==2222==    by 0x4E43A46: event_new (in /usr/lib/x86_64-linux-gnu/libevent-2.0.so.5.1.9)
==2222==    by 0x4009A1: cb_main_listen (main.c:61)
==2222==    by 0x4E41F93: event_base_loop (in /usr/lib/x86_64-linux-gnu/libevent-2.0.so.5.1.9)
==2222==    by 0x400933: main (main.c:49)
==2222==
==2222== LEAK SUMMARY:
==2222==    definitely lost: 272 bytes in 2 blocks
==2222==    indirectly lost: 8 bytes in 1 blocks
==2222==      possibly lost: 0 bytes in 0 blocks
==2222==    still reachable: 0 bytes in 0 blocks
==2222==         suppressed: 0 bytes in 0 blocks
==2222==
==2222== For counts of detected and suppressed errors, rerun with: -v
==2222== ERROR SUMMARY: 2 errors from 2 contexts (suppressed: 2 from 2)

메모리 누수가 확인되는 지점이 두 곳이 보인다. 모두 event_new 를 했던 곳이다. 내용인즉, event_new() 를 통해 생성한 event를 따로 정상 삭제하지 않았다는 내용이다.
나는 timer 에 등록할 경우, 시간이 되어 함수가 작동할 경우, libevent 에서 자동으로 메모리를 해제해 주는 줄 알았다.
하지만 아니었다…

결국, 소스를 다음과 같이 수정하였다.

/*
* main.c
*
* Copyright 2014 Sungtae Kim <pchero21@gmail.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
* MA 02110-1301, USA.
*
*
*/

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <event2/event.h>

typedef struct _wctx
{
struct event_base*  w_base;     // main base
struct event*       w_event;    // work event
} wctx;

static void cb_main_listen(__attribute__ ((__unused__)) int fd, __attribute__ ((__unused__)) short event, void *arg);
static void cb_process(__attribute__ ((__unused__)) int fd, __attribute__ ((__unused__)) short event, void *arg);
static void cb_process_detail(__attribute__ ((__unused__)) int fd, __attribute__ ((__unused__)) short event, void *arg);

int main(int argc, char **argv)
{

wctx* work = calloc(1, sizeof(wctx));

work->w_base = event_base_new();
struct event* main_listen = event_new(work->w_base, STDIN_FILENO, EV_READ, cb_main_listen, work);
event_add(main_listen, NULL);

event_base_dispatch(work->w_base);

printf(“System terminate…n”);
event_free(main_listen);
event_base_free(work->w_base);
free(work);

return 0;
}

static void cb_main_listen(__attribute__ ((__unused__)) int fd, __attribute__ ((__unused__)) short event, void *arg)
{
wctx* work = (wctx*)arg;
//    struct event* w_event = evtimer_new(work->w_base, cb_process, work);
work->w_event = evtimer_new(work->w_base, cb_process, work);

struct timeval  tVal;
tVal.tv_sec = 0;
tVal.tv_usec = 0;
//    evtimer_add(w_event, &tVal);
evtimer_add(work->w_event, &tVal);
}

static void cb_process(__attribute__ ((__unused__)) int fd, __attribute__ ((__unused__)) short event, void *arg)
{
printf(“Process!!n”);
wctx* work = (wctx*)arg;
//    struct event* w_event = evtimer_new(work->w_base, cb_process_detail, work);
evtimer_assign(work->w_event, work->w_base, cb_process_detail, work);

struct timeval  tVal;
tVal.tv_sec = 0;
tVal.tv_usec = 0;
//    evtimer_add(w_event, &tVal);
evtimer_add(work->w_event, &tVal);
}

static void cb_process_detail(__attribute__ ((__unused__)) int fd, __attribute__ ((__unused__)) short event, void *arg)
{
printf(“Detail Process!!n”);
wctx* work = (wctx*)arg;
event_free(work->w_event);
}

소스 수정 후, 다시 valgrind 를 돌려보았다.

pchero@MyGalaxy:~/workspace/Study/Program/LibEvent/memory_leak$ echo “Hello” | valgrind –leak-check=full ./main
==2440== Memcheck, a memory error detector
==2440== Copyright (C) 2002-2012, and GNU GPL’d, by Julian Seward et al.
==2440== Using Valgrind-3.8.1 and LibVEX; rerun with -h for copyright info
==2440== Command: ./main
==2440==
Process!!
Detail Process!!
System terminate…
==2440==
==2440== HEAP SUMMARY:
==2440==     in use at exit: 0 bytes in 0 blocks
==2440==   total heap usage: 11 allocs, 11 frees, 1,720 bytes allocated
==2440==
==2440== All heap blocks were freed — no leaks are possible
==2440==
==2440== For counts of detected and suppressed errors, rerun with: -v
==2440== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 2 from 2)

결과는 성공!

문제 해결의 포인트는 다음과 같았다.

1. event_new()/evtimer_new() 의 사용을 줄이는 것. 가능하면 event_assign()/evtimer_assign() 을 사용하도록 했다.
2. new() 가 있으면 free() 도 해 준다는 것.

C++ man page on linux

그동안 막연하게 리눅스에서도 C++ man page 를 볼 수 있겠지… 하는 생각이 있었다.
하지만 실제로는 한번도 C++ man page 를 사용해본 적이 없었다.

사용 하는 방법을 몰라서였다…

그러던 중 오늘 kldp 에서 C++ man page 보는 방법이 올라와서 여기에 포스팅한다.

 

ubuntu 의 경우, 아래의 패키지를 설치해야 한다.

pchero@MyGalaxy:~$ sudo apt-get install libstdc++6-4.7-doc

뒤쪽의 버전 정보는 그때그때 맞춰서 바꿔줘야 한다. 🙂

그리고, 실제로 man page 를 보기 위해서는 다음을 입력하면 된다.

pchero@MyGalaxy:~$ man std::string
pchero@MyGalaxy:~$ man std::iostream

출처: https://kldp.org/node/141572

 

Using wsdl files on C

회사 프로젝트로 wsdl 파일을 이용한 웹 서비스 프로그램을 제작해야 할 일이 있었다.

물론 사용 언어는 C.
먼저 wsdl 파일들에서 header 파일을 추출해야 했다.

이를 위해서 사용한 프로그램은 wsdl2h

 3개의 wsdl 파일에서 header 파일을 추출해야 했다.

다음의 명령어를 사용했다.

$ wsdl2h -c -o SoapEnv.h SendSms.wsdl

$ wsdl2h -c -n SendSms -o SendSms.h SendSms.wsdl

$ wsdl2h -c -n ReceiveSms -o ReceiveSms.h ReceiveSms.wsdl

$ wsdl2h -c -n SmsNotificationManagerService -o SmsNotificationManagerService.h SmsNotificationManagerService.wsdl

위의 명령어를 잘 보면 추출해야 하는 wsdl 파일은 3개인데, 사용한 명령어는 총 4개이다.
정확히는 SendSms.wsdl 파일에서 두개의 header 파일을 추출했다.

 이유는 실제 컴파일과 라이브러리 구현에 사용할 gsoap를 위해서 기본 header 파일이 필요했기 때문이다. 
그리고, 위에 나타낸 옵션 중, −c 옵션은 c 언어를 위한 헤더 파일 생성 옵션, −n 옵션은 namespace 영역을 구분짓기 위해 사용한 옵션이다.

하나 이상의 wsdl 파일을 사용한 라이브러리 제작시, −n 옵션이 특히 중요한데, 이유는 −n 옵션 없이 wsdl2h 를 사용할 경우, 여러개의 wsdl 사용시 서로 혼합된 namespace 영역을 사용하기 때문이다.
이는 나중에 wsdl 파일을 추가 하게 될 경우, 프로그램 코드상에서 호출되는 메소드 이름이 변경이 되는 등 여러모로 곤란이 생긴다. 때문에 반드시 필요한 옵션이다.
ref: http://www.cs.fsu.edu/~engelen/soapdoc2.html#tth_sEc8

wsdl2h 를 사용하면 각각의 헤더 파일들이 만들어 진다. 이렇게 만들어진 헤더파일들을 이용해 실제 C 에서 사용 가능한 코드로 만들어줘야 한다.이를 해주는 프로그램이 soapcpp2 이다. 이번 프로젝트에서 다음의 명령어/옵션을 사용했다.

$ soapcpp2 -C -c SoapEnv.h

$ soapcpp2 -C -c -n -pSendSms SendSms.h

$ soapcpp2 -C -c -n -pReceiveSms ReceiveSms.h

$ soapcpp2 -C -c -n -pSmsNotificationManagerService SmsNotificationManagerService.h

나는 이 명령어를 makefile 안에 넣어 두었다. 그리고 매번 컴파일을 실행할 때마다 위의 명령어가 자동으로 시작되도록 구성했다.
왜냐하면 soapcpp2 프로그램의 버전때문이었다. 

soapcpp2 프로그램으로 생성되는 파일들은 바로 C 로 컴파일이 가능한 *.c 파일과 *.h 파일들이다.이를 이용해서 라이브러리를 생성하고, 링크시켜서 사용하기 위해서는 libgsoap.a 파일과 같은 gsoap 라이브러리가 추가로 필요하다.
그런데, 만약 매번 soapcpp2 프로그램을 통해서 *.c 와 *.h 파일을 생성하지 않는다면, 이미 생성되어있는 파일들을 이용해서 라이브러리를 만들고 gsoap 라이브러리와 링크를 할텐데..
 때, *.c/*.h 파일을 생성한 soapcpp2 의 버전과 링크되는 libgsoap.a 파일의 버전이 서로 맞지 않는다면 링크가 안되는 문제가 발생한다.
이런 이유로 매번 컴파일을 진행할 때마다 위의 명령어를 통해서 새로이 stub 코드를 작성하도록 구성했다.

그리고, wsdl2h 사용할 때와 마찬가지로, namespace 영역 구분을 위해 −n 옵션과 −p 옵션을 추가했다. 그리고, gcc 컴파일을 진행시 −D 옵션을 이용해 Define 을 별도로 걸어주었다.
이는 지정된 namespace 영역을 사용하기 위해 필요한 조치이다.
makefile 안에 다음과 같은 Define 을 넣어주었다.ref: http://www.cs.fsu.edu/~engelen/soapdoc2.html#tth_sEc19.36

DEFS                    = -DWITH_OPENSSL -DWITH_NONAMESPACES

마지막으로, 왜 그런지는 모르겠는데..  이상하게 gsoap 라이브러리를 추가시, 자꾸 세그먼트 폴트가 났다.
계속 방법을 찾다가 제일 마지막에 gsoap 라이브러리를 추가 해야 오류가 나지않는 이상한 현상을 확인했다.
왜그러는지는 모르겠으나.. 다른 해결할 방법을 찾지 못했다.

LIBS            =  -lzmq -lczmq -luuid -ljansson -lssl -lcrypto -lpthread -lcurl -lgsoapssl -lgsoap

C++ Int, char, double, 등등의 자료형 사이즈..

C++ 에서의 자료형의 크기는 컴파일러에 따라 좌우된다고 알고 있었다.

틀린 내용은 아니였지만.. 정확하지는 않았다.

아래 링크에서 정확한 내용을 알 수 있었다.

정확하게는… 크기는 정해져 있지 않지만, C++ 표준에서 지정하는 최소 사이즈가 있다는 것이다.

The C++ standard does not specify the size of integral types in bytes, but it specifies minimum ranges they must be able to hold. You can infer minimum size in bits from the required range and the value of CHAR_BIT macro, that defines the number of bits in a byte (in all but the most obscure platforms it’s 8).

출처 : http://stackoverflow.com/questions/589575/size-of-int-long-etc