채팅 서버 프로그램을 제작하고 있는데 ??

짜라란의 이미지

채팅 서버 를 제작하고 있습니다.'

네트워크는 쓰레드를 이용하지 않는 epoll을 이용하여 제작하였는데

한 유저가 접속하면 객체를 생성하여 리스트로 관리합니다.

테스트로 클라이언트에서 100번 접속을 하였습니다.

물런 객체도 100개 생성하여 리스트에 넣는데 이 부분에서

부하가 너무 많이 걸리네요. 일단 이 부분이 그렇게 부하가 심한지 궁금하구요.

그냥 메모리에 객체를 만들어 리스트로 관리하는 것 보다

DB에 유저 정보를 저장하고 빼면서 관리하는 것이

부하가 더 안걸리는지도 궁금합니다.

답변 부탁드립니다.

좋은 하루 되세요.

ctcquatre의 이미지

흠.. 제가 지금 그렇게 만들어 쓰고 있는데..
부하는 없더군요.
100명이 동시접속해도 cpu부하는 3~5프로도 채 안됩니다.(펜4 2.8, 512ram)
로직이 잘못짜여졌으리라 봅니다.

성능을 신경쓴다면 메모리 alloc에도 신경을 쓰셔야 될겁니다
2의 멱승의 크기로 클라이언트 자료형을 정의해주세요.
패딩도 해야될꺼고.

DB와의 연동은 안해봐서 모르겠지만.
하드웨어적으로 받쳐준다면 당연히 메모리가 훨 빠를겁니다.

Chaos to Cosmos,
Chaos to Chaos,
Cosmos to Cosmos,
Cosmos to Chaos.

짜라란의 이미지

객체는

#define LEN_NAME 20
#define LEN_STATE 5
#define LEN_IP 15

class CUserData
{
public:
CUserData();
virtual ~CUserData();

public:
int m_nCltSocket;
char m_strCltIP[LEN_IP];

char m_strUserID[LEN_NAME];
char m_strPageID[LEN_NAME];
char m_strUserSex[LEN_NAME];
char m_strUsername[LEN_NAME];
char m_strUserAge[LEN_STATE];

char m_strMtmNow[LEN_STATE];

CUserData* m_pNext;
CUserData* m_pPrev;
};

이고

접속하면

CUserData* pUser = new CUserData;

if(pUser)
{
pUser->m_nCltSocket = nSock;
strcpy(pUser->m_strUserID, strUserID);
strcpy(pUser->m_strPageID, strPageID);
strcpy(pUser->m_strUserSex, "M");
strcpy(pUser->m_strUsername, "아무개");
strcpy(pUser->m_strUserAge, "19");
strcpy(pUser->m_strMtmNow, "0");

usrList.AddUser(pUser);
}

입니다. 이 부분을 빼면 빨리 처리하는데

객체 만드는 데서 버벅 거리네요..

서버는 펜4 1.3 256 메모리 입니다.

로직에 문제가 있나요 ?

kihongss의 이미지

짜라란 wrote:
채팅 서버 를 제작하고 있습니다.'

네트워크는 쓰레드를 이용하지 않는 epoll을 이용하여 제작하였는데

한 유저가 접속하면 객체를 생성하여 리스트로 관리합니다.

테스트로 클라이언트에서 100번 접속을 하였습니다.

물런 객체도 100개 생성하여 리스트에 넣는데 이 부분에서

부하가 너무 많이 걸리네요. 일단 이 부분이 그렇게 부하가 심한지 궁금하구요.

그냥 메모리에 객체를 만들어 리스트로 관리하는 것 보다

DB에 유저 정보를 저장하고 빼면서 관리하는 것이

부하가 더 안걸리는지도 궁금합니다.

답변 부탁드립니다.

좋은 하루 되세요.

리스트로 관리할 유저 정보를
DB로 관리하는게 더 오버헤드가 클것 같군요. :cry:
클라이언트가 접속할때마다
객체를 생성하지 마시고
객체풀을 고려해보세요.

ctcquatre의 이미지

저역시 link list로 클라이언트정보를 관리하고 있습니다.

제가 부딛혔던 문제는 link list일경우 index를 지원할수 없기때문에.
새로운 자료형의 추가나,삭제, 수정시에 그 자료형을 찾기위해
선형검색을 해야한다는데에 있었습니다.
뭐 트릭을 써서 해결하긴 했지만 여전히 일부분의 문제는 있었습니다.

흠..특별히 위에소스에서 문제를 잘 모르겠네요.

클래스객체를 동적으로 생성할때 할당되는 자료형의 크기를 계산해보세요.

memory reallocation이 빈번하게 이루어질 경우에
생각보다 오버해드가 심합니다.
그래서 위에분 말씀처럼 객체풀을 추천하는거구요.
위의 소스를 보고는 그것밖에 말씀드릴수가 없네요.

위에서 유저id,유저페이지 id의 경우 문자열같은데 패킷으로 전송받는것이지요?
실수로 널 종료문자열 처리를 안하는경우가 간혹있는데.
이럴땐 운좋으면 segment fault 를 내지만 그렇지 않으면 그대로
진행되는 경우도 있습니다.. 물론 이경우에도 쓸대없는 부하를 잡아먹고요.

하나하나 소스코드의 값을 측정해보세요.. 아는게 여기까진지라..
더이상 뭐라 할말도 없네요.. 좋은 하루되세요.

Chaos to Cosmos,
Chaos to Chaos,
Cosmos to Cosmos,
Cosmos to Chaos.

happycat의 이미지

저라면 풀 형태로 객체를 미리 만들어 놓고 시작하겠습니다. :)

그리고 리스트 형태라면 나중에 ID에 해당하는 유저를 찾기 힘들텐데 ( O(n) )

map이나 hash_map을 사용하는 게 어떨까요? ( O (log n) or O (1) )

string을 쓰지 않으신 걸 보니 STL을 사용하시지 않은 것 같은데

STL이 의외로 좋을 수도 있습니다.. 편하기도 편하지만 string은

copy on write가 지원되기 때문에 저런 코드에서도 실제로 복사가

일어나지 않고 넘어갈 수도 있죠 :)

tinywolf의 이미지

stl의 vector를 사용하면 편할 것같은데요..

vector를 살펴보면 초기에 몇개까지 미리 영역을 마련해 둘 것인지..

어느정도로 빈공간이 남았을 때 공간을 늘릴 것인지..

정해 주는게 있었던 걸로 기억합니다.

그걸로 미리 시작할 때 넉넉히 잡아놓고 시작하면 충분할 듯 생각되는군요.

id나 특정 코드만으로 검색이 이루어진다면 hash_map도 상당히 좋은 방법이 될 수 있겠군요..

ㅡ_ㅡ;

ssehoony의 이미지

new 연산자가 상당한 cost 를 유발하는건 맞지만
100명 정도의 사용자를 위해 만드는건 너무나 가벼운 일이죠.

객체가 문제가 아니라면, list.add 에 문제가 있을 듯 하군요.
테스트로 객체 생성은 그대로 두고 list.add 부분만 제거해서 테스트 해보세요. 그래도 문제가 있다면 클래스 디폴트생성자를 의심해야 할 것 같군요.

profilering이 가능한 환경이라면 profilering을 해보시는게 가장 쉬울 듯 하군요.

ssehoony의 이미지

happycat wrote:
STL이 의외로 좋을 수도 있습니다.. 편하기도 편하지만 string은

copy on write가 지원되기 때문에 저런 코드에서도 실제로 복사가

일어나지 않고 넘어갈 수도 있죠 :)

string 이 copy on write 가 지원되기도 하다니 좋군요.
혹시 이에 관련한 자료가 있는 곳을 알려주실 수 있나요?

doldori의 이미지

string에서 COW를 쓰는 구현이 있다니 어느 것인지 저도 궁금하네요.
이와 관련해서 reference counting은 많이들 쓴다고 하더군요.
gcc는 RC를 쓰는 반면 STLport나 Dinkumware는 쓰지 않습니다.

doldori의 이미지

짜라란 wrote:
CUserData* pUser = new CUserData;

if(pUser)
{
    /* ... */
}

new가 메모리 할당에 실패하면 널 포인터를 반환하는 것이 아니라 std::bad_alloc
예외를 던집니다. 따라서 이것은 올바른 처리 방법이 아닙니다.
널 포인터를 반환하는 형태도 있긴 합니다.
#include <new>

CUserData* pUser = new(std::nothrow) CUserData;
if (!pUser) { /* ... */ }   // error handler
happycat의 이미지

음.. 제 기억엔 effective c++에서 'COW로 구현할 수도 있다'

라는 말을 본 후 직접 테스트 해서 알았던 사실 같습니다.

copy on write가 실제로는 복사가 일어나지 않고 뭔가 내용이

변경될 때 복사가 일어난다는 것 맞지요..?

혹시 제가 뭔가 잘못 이해하고 있다면 지적해 주시면 감사드리겠습니다.

#include <string>
#include <iostream>

using namespace std;

int main()
{
    string a = "abc";
    string b = a;

    cout<<(int)a.c_str()<<", "<<(int)b.c_str()<<endl;

    b[3] = 'a'; // 대입을 하지 않아도 결과는 같습니다.
   
    cout<<(int)a.c_str()<<", "<<(int)b.c_str()<<endl;

    return 0;
}


결과:

134524988, 134524988
134524988, 134525004
happycat의 이미지

아 참.. g++ 3.4.2로 컴파일 했습니다.

그 이전 컴파일러에서도 동일한 결과였던 것으로 기억합니다.

doldori의 이미지

이 코드만으로 COW를 쓰는지 여부를 판단하기는 힘들 것 같습니다.
코드를 좀 바꿔보죠.

//  b[2] = 'a'; // 대입을 하지 않아도 결과는 같습니다. 
    char c = b[2];

이때는 read이므로 출력이 같아야 할 것인데 그렇지 않습니다. 이것으로 미루어보면
gcc는 RC만을 쓰는 것 같습니다. COW를 구현하려면 proxy class를 쓰는 것이
자연스러운 방법인데 gcc에는 그런 것이 보이지 않는군요.

좀 깐깐한 얘기지만 포인터를 int로 변환하는 결과는 정의되지 않습니다. char*의
주소를 출력하려면 void*로 캐스팅 하십시오.
cout << (void*)a.c_str();

happycat의 이미지

doldori wrote:

//  b[2] = 'a'; // 대입을 하지 않아도 결과는 같습니다. 
    char c = b[2];

주석에도 달아 놓았지만 단순히

b[2];

이렇게만 해 놓고 대입은 하지 않아도 가르키는 포인터가 변합니다.

operator[]의 정의는 아래와 같은데요..

reference operator[](size_type n) 

즉, reference를 돌려 주기 때문에 값을 바꿀 가능성이 있다고 보고 이 시점에 copy를 한다고 봅니다. reference를 가져가서 대입 연산을 할 지, 그냥 값만 사용하고 갈 지는 [] 연산자가 불릴 시점에서는 알 수가 없기 때문이죠.

값을 바꿀 가능성이 없는 연산자들이 호출된 이후에는 c_str()이 가르키는 포인터 값이 변하지 않습니다. find()같은 연산자들을 호출해 보면 알 수 있지요..

결론적으로, COW를 지원하긴 하되, 완전하지는 않다라고 보는 것이 맞겠습니다. 하지만, =이 불리는 시점에서는 복사가 일어나지 않는 것은 맞고, 최대한 시점을 늦춰 복사가 일어나기 때문에 C 스타일의 복사보다는 효율적이라고 생각합니다. 때문에 추천드린 것이고요 ^^

doldori wrote:

cout << (void*)a.c_str();

지적 감사드립니다. 퇴근 10분전에 급히 작성하며, 이 부분에서 뭔가 이상하다는 느낌이 들었었는데, 저 코드가 더 맞을 거 같네요. int로의 캐스팅이 undifined인 이유는 int와 주소가 크기가 다를 수 있기 때문이겠죠..?(하나는 32bit면서 하나는 64bit일 수 있다거나..)

happycat의 이미지

doldori wrote:

이때는 read이므로 출력이 같아야 할 것인데 그렇지 않습니다. 이것으로 미루어보면
gcc는 RC만을 쓰는 것 같습니다. COW를 구현하려면 proxy class를 쓰는 것이
자연스러운 방법인데 gcc에는 그런 것이 보이지 않는군요.

아.. 그리고 copy on write를 구현하기 위해선 reference counting은 필수로 되고 있다고 보여집니다. :) 물론 아시겠지만 둘이 동떨어진 개념은 아니라고 생각합니다. reference counting을 한 다는 얘기가, 같은 문자열을 가르킬 때에는 단지 counter만 증가시키겠다는 이야기이고, 만일 같은 문자열을 가르키는 상황에서 수정이 일어나야 한다면 필연적으로 복사가 일어나야 하겠죠..

doldori의 이미지

happycat wrote:
즉, reference를 돌려 주기 때문에 값을 바꿀 가능성이 있다고 보고 이 시점에 copy를 한다고 봅니다. reference를 가져가서 대입 연산을 할 지, 그냥 값만 사용하고 갈 지는 [] 연산자가 불릴 시점에서는 알 수가 없기 때문이죠.

네, 그렇군요. 그것도 COW라고 할 수 있습니다. 저는 operator[]가 대입연산자의
좌변에 올 때만 복사가 일어나는 형태만을 생각했다가 그 점을 놓쳤네요. ^^;

댓글 달기

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