박지훈.임프 님이 쓰신 글 :
: 처제 졸업식으로 나가있던 사이에 문제가 터졌더군요.
: 오전 11시 정도부터 3시까지 약 4시간 정도 서버가 오동작을 했습니다.
:
: 대략적인 원인을 파악했는데...
: 일단 직접적인 원인은, 제가 오늘 새벽에 해피브레이크에 올렸던 오뎅장사 이야기였습니다.
: 이 글의 본문이 무려 100kB나 되는 엄청난 양인데요.
어느 정도 크기 이상의 글은 못 올리게 하고 대신 첨부파일로 올린다거나,
html 태그를 쓸 것인지를 미리 선택하게 하는 방법(적수(JS)보드처럼요)은 어떨까요?
: 이걸 파싱하는데 시간이 너무 많이 걸려서, 한사람만 이 글을 읽는데도 4~5초 정도 걸려야 화면이
: 뜹니다. 그런데 그 4~5초를 못기다린 분이 다시 F5를 눌러서 페이지를 리로드하려고 시도하면
: 그전의 요청이 아직 처리되고 있는 중에 또 한번의 요청이 들어가게 됩니다.
:
: 안그래도 50% 이상의 CPU 타임을 먹고 있었기 때문에, 두개의 쓰레드가 거의 100%에 가까운 CPU를
: 점유하게 되는데, 이 상태에서 몇번 더 F5를 눌러버리면 사실상 서버가 뻗어버리게 됩니다.
:
: 본문을 뭘 파싱할 필요가 있겠냐 싶겠지만, 실제로는, 본문에 포함되어 있을 수 있는 링크들,
: 즉
http://www.borlandforum.com 이렇게 쓰기만 해도 자동으로 링크로 인식해야 하고, 또 그외에
: html 태그를 그대로 썼을 경우에 대해서도 처리를 해주어야 하기 때문에 보기보단 파싱이 상당히
: 복잡합니다.
:
: 이 파싱 루틴은 시간을 많이 잡아먹을 수밖에 없는 건데, 최대한 빠르게 처리를 해야 하는거지요.
: 그래서 제가 몇주에 걸쳐 나름대로 최적화를 해서, VCL의 기본 StringReplace() 함수를 그냥 호출
: 했을 때와 거의 비슷한 속도를 내고 있었는데요.
:
: 파싱 대상이 100kB 단위가 되니 시간이 몇초 단위로 늘어지는 것을 어떻게 할 수가 없네요.
: 메모리 재할당 한번도 없이 문자열의 처음부터 끝까지 선형으로 포인터 검색만 하도록 최적화시킨
: 거라서 어셈블리를 쓰지 않는 한은 더이상 최적화할 방법이 없구요.
문자열을 char*의 선형(flat) 배열 형태로 쓰는 것이 최적인 것은 아닙니다.
빌더6에 기본으로 들어있는 STLport(물론 빌더5 이하에서도 쉽게 설치됩니다.)의 rope를 써보시는 건 어떨까요.
rope는 문자열 전체를 대상으로 한 동작에 최적화되어 있는 자료구조로서,
substring들의 트리로 되어 있습니다.
대입(assignment), 연결(concatenation), 부분 문자열(substring) 등의
연산이 문자열의 길이에 상관없이 수행됩니다.
http://www.sgi.com/tech/stl/Rope.html
에 의하면 10MB의 rope 중간에 캐릭터 하나를 삽입할 때
10 microseconds(= 10E-6 초)가 걸린다는 군요.
반면에 문자를 하나씩 바꾸는 동작은 오히려 무지 느립니다.
위 사이트의 예제를 보여드리죠.
crope r(1000000, 'x'); // crope is rope<char>. wrope is rope<wchar_t>
// Builds a rope containing a million 'x's.
// Takes much less than a MB, since the
// different pieces are shared.
crope r2 = r + "abc" + r; // concatenation; takes on the order of 100s
// of machine instructions; fast
crope r3 = r2.substr(1000000, 3); // yields "abc"; fast.
crope r4 = r2.substr(1000000, 1000000); // also fast.
reverse(r2.mutable_begin(), r2.mutable_end());
// correct, but slow; may take a
// minute or more.
: 사실 작년부터 이런 문제가 생길 가능성 정도는 예측을 하고 있었기 때문에, 다른 대안을 모색하고
: 있었는데요. 가장 좋은 방법은, 파싱이 끝날 때까지 응답을 쌓아두지 말고 파싱 중간에 분석된
: 부분이 어느정도 되면 빨랑빨랑 보내주는 것입니다.
:
: 이렇게 하면 기존보다 더 빠른 시간내에 일단 사용자에게 현재 분석이 된 부분들을 계속 보내주기
: 때문에 사용자가 답답할 이유가 없지요.
:
: 근데.. 현재로서는 이게 생각보다는 간단하지 않게 되어있답니다.
: 포럼 게시판에는, 제가 만든 여러가지 웹처리용 컴퍼넌트들이 포함되어 있는데, 그 관계가 좀 복잡합니다.
: 그래서 아직 처리를 못했던 거구요. 웹브로커의 기반 구조를 약간 바꾸어야 하는 문제라서...
:
: 그래서리... 일단은, 눈물을 머금고(?) 해피브레이크의 오뎅장사 이야기를 삭제했답니다.
: 100kB 단위의 엄청난 본문을 올리면 또 문제가 생기겠지만, 포럼 역사상 본문 100kB가 올라온 적은
: 이번이 처음이니까 당분간은 문제가 없을거구요.
:
: 게시판의 구조 개선 문제는.. 이달 말쯤 되어야 할 거 같습니다.
:
: 그럼...
:
: