wirte(120k 정도데이터) 로 보내면 속도가 느릴때 .. 어떻게 해?
글쓴이: shean0 / 작성시간: 화, 2004/04/20 - 4:31오후
안녕하세요..
프로그램을 하나 개발했는데.. 속도 때문에 최적화에 애를 먹고 있습니다.
일반적으로 보내는 데이터가 작을때는 문제가없는데.. 120k 정도되는 양을 보낼때는 tcp프래그멘테이션때문인지... 넘 느립니다.
이거 해결하려면.. 어떻게 해야하죠?
write(real-data)를 1024만큼 잘라서..
write(real-1);
write(real-2);
write(real-3);
이렇게 호출해야하는지... 이거참.. 난감하군요..
조언 부탁드립니다.
먼저 전체 소스구조와 시간을 표현하자면. 전체로직은 Recv_DATA에서 데이터를 받고. Make_Send를 호출 parse_data()에서 받은데이터 파싱하고. make_data()에서 real-data만들고 write_header(); 헤더 만들어서 보내고. write_data(real-data); 보낸다. 이상입니다
소스는
int Magic10Main(int soknum) { int pid = ((getpid()) % 100000); Trace_log(start); //로그 Recv_DATA(); // shutdown(sok,SHIT_RD); //다시read - block대기하므로.. Make_Send(); Trace_log(sendAll); //로그 shutdown(sok,SHUT_WR); read(sok,buf,1);//클라이언트 특징에의해서::client가 먼저 끝을때까지 기다린다 Trace_log(All-END); // } Make_Send() { parse_data(); Trace_log(cmd); //로그 make_data(); Trace_log(send-start); //로그 write_header(data-header); write_len(real-data); //==>여기부터문제가 됩니다. } write_len(data) { while(1) { write(sok,data[pos],nleft); .../* 다 보낸다. */ } }
[start:20305]:[16:08:21] //시작시각 현재 21초 [=>데이터 read하고] [20305:cmd:13]:[16:08:21] //cmd:13번 추출 :현재 21초 [=>데이터 모두 만들고] [send-start]:[16:08:21] //데이터보내기 시작 //즉 데이터 다 만들때가지는 금방인데..... //만들어지는 헤더는 고정길이 20바이트이고, 만들어서 보냅니다. [sendAll:20305]:[16:08:36] // 15초라니.. 데이터크기는 12k입니다. [ALL-END:20305 ]:[16:08:36]
이렇게 느리게 서버에서 : 데이터 write가 이루어지고.
클라이언트단에서는 테스트해보면
헤더는 금방 받아들여지는데..
즉. 요청->헤더 recv 까지는 금방이지만.
데이터 recv가 너무 느립니다...
이 현상을 어디서 부터 접근해서..해결할수 있을까요?
실제 write_len소스는
unsigned int write_len(int sok,char *buf,unsigned int len) { unsigned int nleft, w_len; char *p; nleft=len; p=buf; while(nleft>0) { w_len=write(sok,p,nleft); if(w_len<0) { return ERROR; } else if(w_len==0) { ; } else ; nleft -=w_len; p +=w_len; } return (len-nleft); }
Forums:
안녕하세요..지나가다 얼핏 답글이 없어서.. 참고하시라고 올립니다
안녕하세요..
지나가다 얼핏 답글이 없어서.. 참고하시라고 올립니다..
참고만 하시길( 언제나 오답의 봉변을 회피하기위한 )
제가보기엔 Write시에 블러킹 되는것같습니다..
상대편이 Recv를 다해주지 않은것같네요..
보낸만큼 recv를 해주어야합니다.. 블러킹 상태라면..
send에서 상대방이 다 받을때까지 또는 타임아웃 상태까지
대기하게 됩니다.. 물론 이것은 소켓옵션에 따라 send옵션에
따라서 결과가 달라질수 있습니다.. 그럼 ^^
' 형식이 내용을 규정한다. '
tcp kernel buffer세팅을 조절해 보세요.
저는 질문하신 분의 문제를 버퍼 문제로 찍어보겠습니다! :roll:
tcp kernel buffer크기를 조절해 보시기 바랍니다.
tcp는 자체적으로(커널내부의 프로토콜스택에서) reliability를 제공하고,
flow control과 congestion control을 제공합니다. 이때, 양단간에
통신버퍼사이즈가 잘~ 맞지 않으면 느린경우가 발생하기도 합니다.
특히, ftp와 같이 대량의 데이터를 전송하는 경우에 더 심합니다.
보통 튜닝하게 되지요.
부연하자면, 통신이란게, 혼자하는게 아니라 tcp통신에서는 적어도 둘(2)이 하지요.
보내기만 열심히 보낸다고 되는게 아니라, 받는측의 수준도 생각해 주어야 합니다.
받는 컴퓨터가 무지 빠르고 메모리 많다고 좋은게 아니고 통신버퍼세팅이
성능을 좌우하는 경우가 많습니다. 즉, 파라메터를 잘 맞춰야 하는데요.
보내고, 받는측의 파라메터를 맞출때, 가장 중요한 것은 네트워크
밴드위쓰(10메가랜 또는 100메가랜등등)도 있지만, 버퍼가 아주 중요한
요인이 되기도 합니다.
송신측, 수신측 둘다 소켓을 생성할때, 기본 소켓버퍼사이즈가 있는데,
(getsockopt()로 알아볼수있습니다)
이를 늘려보세요.
물론 실험적으로 늘리는 방법이 있고, 자동으로 이를 측정해서 늘리는 방법도
있을수있습니다.
일단은 실험적으로 늘려가면서 테스트를 해보세요.
당연히 클라이언트+서버 둘다요.
------------------ P.S. --------------
지식은 오픈해서 검증받아야 산지식이된다고 동네 아저씨가 그러더라.
아마 Nagle Algorithm과 Delayed ACK 의 영향을 받는 것 같습니
Snader의 Effective TCP/IP Programming 책
Tip 24. When Possible, Use One Large Write Instead of Multiple Small Write
에 보면 비슷한 현상에 대해서 기술하고 있습니다.
delayed ack의 timeout을 일반적인 200 ms로 잡고,
MSS를 1440으로 잡고 말씀하신대로 120K 의 데이터를 보낸다면
(본문에는 120K 라고 하신 것과 12K라고 하신 것이 있는데, 120K죠?)
Nagle 알고리즘과 Delayed ACK이 작용을 하면
(120K / 1440) * 200 ms = 약 16.7 초로
말씀하신 것과 비슷한 시간이 걸립니다.
책에는 Nagle 알고리즘을 disable 시켜서 이 문제를 *피해갈* 수 있다고 되어있습니다.
(Nagle 알고리즘을 disable시켰을 때의 부작용에 대해서도 언급하고 있습니다.)
의 코드로 Nagle 알고리즘을 disable시킬 수 있습니다.
(이 옵션에 대한 설명은 Stevens의 UNP vol.1 2판의 7.9절에도 나와있습니다.)
이상이 책에 언급된 내용이고요,,
책과 올리신 글을 읽고 의문이 생겼습니다.
말씀하신 것과 같은 문제가 발생할 수 있다는 것은 알고있었는데,
실제로 그와 같은 일을 제가 겪어보지는 못했었거든요.
한 1M 되는 데이터를 한 번에 다른 장비로 전송하는 테스트 코드에서도
delay없이 그대로 전송이 되었었거든요.
각본대로라면 client에서 계속 MSS씩 전송을 하면 서버쪽에서
delayed ACK에 걸려서 ACK을 보내주지 않는 경우
윈도우가 꽉차서 클라이언트에서는 더 이상 데이터를 못 보내고
서버쪽이 timeout 걸려서 ACK을 보낼때까지 기다려야 하는 상황이
발생해야 할텐데요.
이런 경우에는 {(보낼데이터크기 / MSS) / (서버윈도우크기 / MSS)} * 서버 delayed ACK timeout
만큼의 시간이 적어도 걸릴 것일텐데 그렇지가 않더라고요.
플랫폼을 윈도우-윈도우, 윈도우-Solaris, Solaris-윈도우로 해도 문제없이 잘 수행되고요.
그래서 뭔가 있나 ndd로 Solaris쪽 TCP tunable 을 살펴봤더니
tcp_deferred_ack_interval과 tcp_deferred_acks_max
라는 것이 있었습니다.
(윈도우쪽 TCP tunable 볼 수 있는 방법이 있나요?)
tcp_deferred_ack_interval은 delayed ack의 timeout 값을 지정하는 것이고,
값은 default로 100 ms 로 되어 있었습니다.
Solaris 메뉴얼에 tcp_deferred_acks_max 는
와 같이 설명되어 있습니다.
(Solaris 10의 메뉴얼에서 가져와서 remote라는 용어가 있습니다.
Solaris 7에는 remote와 directly connected 호스트를 구별하지 않습니다.)
중요한 것은 delayed ack의 max값이 있어서
그 이상으로 데이터(MSS갯수)가 들어오면
piggyback할 서버쪽의 응답이없고 delayed ACK의 timeout이 발생하지 않더라도
ACK이 전송된다는 것입니다.
그리고 이 값은 최대값을 지정하며, connection별로 동적으로 계산됩니다.
(실제로 패킷을 잡아보았더니 초기에는 2였는데 3으로 올라갔습니다.
넷웍상황이 좋을수록 어느 정도까지는 값이 커지는 것이 유리하겠죠?)
아마 이런 이유로 제가 테스트했던 환경에서는 전송시간 지연이 나타나지 않은 것 같습니다.
제 테스트환경은 Solaris 7 (Sparc), Win 2000, Win XP, 100M Ethernet Lan 이었습니다.
아, 그리고 비슷한 문제를 다른 분이 겪은 적이 있는데(AIX 5L),
그 경우에는 버퍼 크기를 조정(줄여서)해서 문제가 사라졌던 적이 있습니다.
반대의 경우로는 Snader책 Tip 32: Understand the Effects of Buffer Size 에서 send buffer를 최소 3 * MSS로 하는 것을 추천하고 있습니다.
(일반적으로 기본값이 그 정도는 됩니다. -_-)
참고로 버퍼크기 조정은 listen이나 connect 하기 전에 해 줘야 한다는군요.
저도 비슷한 경험을 한 적이 있습니다.Client-Server 프
저도 비슷한 경험을 한 적이 있습니다.
Client-Server 프로그램에서 데이터를 주고 받는데,
1. 받는측이 windows 2000 (보내는측하고 관계없음)
2. 보내는 data size가 1463 인가 보다 작을때
200ms 의 delay가 생기는 것이었습니다.
처음에 이게 왜생기는지 별의별 테스트를 다 해보다가 (device driver 에서 생기는 문제여서..결국 문제는 device driver의 문제가 아니라 windows tdi 구현이 그렇더군요..) 위의 조합을 알아냈습니다.
그때 찾아보다가 이것이 MS Windows 2000의 ack timeout 이라고 하던데, 그때 당시에는 아주 data size를 조정할수도 없고, delay가 성능에 심각한 영향을 미치는 것이었습니다.
그래서 결국 client/server단에서 서로의 os를 알아내서 보내도록 protocol을 수정해서 (프로그램이 여러 플랫폼에서 돌아가기 때문에...) 위의 조건이 만족되면 data 크기가 1463이 되도록 쓰레기값을 집어넣어 수정했습니다.
참고 바랍니다.
PS: socket option 수정으로 해결 가능한지는 저도 모르겠습니다. 그때 당시에 문제는 device driver에서도 같은 network 문제가 발생하고, device driver에서는 socket option을 수정하는 interface가 없었거든요. 그래서 해결방안으로 고려되지 않았죠.
테스트는 해보셨나요?
테스트를 해보셨는지 궁금하군요.
tcp 커널 버퍼크기의 중요성은 tcp의 ARQ(Automatic repeat ReQuest )에서
사용한 방법론에서 기인합니다. tcp의 구현에 따라 차이가 있을 수 있지만,
ACK가 수신되기 전에 송신할수 있는 데이터의 크기를 나타내는 window크기(sliding window algorithm)가
바로 그것입니다.(쉽게 쓰자면, 링버퍼, 환형버퍼-like한 ...거지요. ARQ만 가지고도 오랜 얘기가 됩니다.)
다시 말하자면, "10개를 보내는데, 1개보내고 응답받는게 아니라, 연속적으로 6개를 받고, ACK를 받는다면
성능에 보다 유리하다"는 얘기지요.
이 윈도우 크기는 초기 연결을 위한 3 way-handshaking에서 결정됩니다.
그래서, 윈도우크기 세팅(버퍼크기세팅)이 connect()또는 accept()보다는 빨리 이뤄져야합니다.
테스트의 편의를 위해서 간단한 도구를 하나 소개하겠습니다.
물론 제가짠거 아니고( 관리소홀로 현재 없음)... :oops:
보탬이 되었기를......
* 용어의 혼란이 있을까봐..
커널버퍼 = 커널 내부의 tcp /ip 프로토콜 스택의 송신 또는 수신버퍼
** 참고
또한 응용수준에서의 플로우콘트롤, 즉, 응용수준에서 프로토콜을 만들어서
1024정도로 나누어 전송하고, 응용수준에서 이에 대한 ack를 받게끔 프로그램을(응용 프로토콜) 만드는 것도 방법입니다.
저도 이를 많이 애용하기도 했습니다.
------------------ P.S. --------------
지식은 오픈해서 검증받아야 산지식이된다고 동네 아저씨가 그러더라.
Re: 테스트는 해보셨나요?
먼저 많은 조언해 주신 분들께.. 고개 숙여 감사드리며....
처음 소스는 write_len(sok,real-data[120k바이트]);
이렇게 수정해 보았는데도..별로 효과가 없더군요.[write_len 소스는 위에 있죠^^* ] 하여서 system에 의존하다가..
1024로 짤라서
이 말씀이 이해가 안되어서 이렇게 다시 조언을 부탁드립니다.
아..그리고 당연이 setsockopt이 걸린 프로세스만 영향을 주겠죠? ^^*
언제나 즐프를 꿈꾸며~
좋은 자료가 될 것같아서 링크 올립니다. 번역도 대부분 되어 있
http://www.joinc.co.kr/modules/moniwiki/wiki.php/HighPerformanceNetwork
recv 하는부분도 볼수 없을까요?write부분만 가지고 유추하니
recv 하는부분도 볼수 없을까요?
write부분만 가지고 유추하니까 애매한 문제들이 있어서..
몬가 놓치고 있는게 있지않을까 싶습니다..
' 형식이 내용을 규정한다. '
튜닝 테스트중..
[제목: 튜닝중.. 그런데 accept타임이 긴 이유는??]
말씀해 주신 부분들을 테스트 하고 있구요.
일단 아래 소스처럼 소켓 버퍼 사이즈를 조절하니..눈에 띄게 성능향상이 나왔습니다. 감사드리며.. 이왕에 시작한거.. 조금 더 공부하고자 합니다.. 귀찮아도 ^^*
소켓-사이즈 튜닝분 소스 발췌입니다.
전체 소스는 이런 구조이구.. 현재 버퍼 크기 조절을 통해 테스트 중입니다.
요청하신 recv부분 소스 입니다.
말씀하신 read소스 역시 wirte와 같은 구조입니다.
지금은 많은 분들이 조언해 주신 부분에 대해 살펴보고 있습니다.
그리고 .. 여기에서 한가지.. 문제에 대해 또 다른 조언을 구하고자 합니다.
listen과 accept 타임에 대한 것인데요.
여러 테스트를 하다 보니.. printf("accept time\n"); 이라고
서버에서 accept가 얼마나 빨리 일어나는가에 대해 살펴 보고 있었습니다.
그런데 프로세스 요청이 많을 경우에는 이 시간이 꽤 긴것 같더군요.
책을 찿아보니
listen()인자의 대기수가 이것을 빠르게 하는것두 아닌것으로 나와 있는데.
제가 오해하고 있는것 인가요??
짐작은
tcp_deferred_acks_max ,tcp_deferred_ack_interval 과 어떤 연관이 있지 않을까? 생각하는데.. ack 리턴되고. accept리턴되자 않을까? [상상만....]
"Snader의 Effective TCP/IP Programming 책"저두 구입해서 지금 보고 있습니다.. 이런 부분은 어떻게 테스트 하셨는지?? 최적화 방법은 어떤것이 있는지..부탁드립니다.
결론적으로..
아직 알려주신 문서들을 모두 살펴 보지 못하고.. 또 다시 문의를 드리게 되었네요.
현재는
1 . buf 사이즈 조절해서 테스트
2 . tcp_deferred_acks_max 를 찿아 보았는데..시도하기가 만만찮군요.ㅠㅠ
ndd 역시..
3.
타임아웃에 대한 그냥.. 확인사살?? ^^* 아래 것은 저는 아래와 같이 구현합니다.
4 . 지금 솔라리스 다시 깔고 있습니다.
아무래두 http://dast.nlanr.net/Projects/Iperf/ 이놈 테스트하려구 다운 받고 보니.. 현재 운영중인장비에서 하기는 조금 ..거시기 해서요 ..
테스트 하는데로.. 여기에 공개하겠습니다.
와.. 할거 많네요..
^^* 모든 분에게 감사드리며.. 이만..
언제나 즐프를 꿈꾸며~
일단 사용자 수준의 flow control은 응용프로토콜 설계로 부터 시
일단 사용자 수준의 flow control은 응용프로토콜 설계로 부터 시작합니다.
tcp위에다가 응용 프로토콜을 만드는것입니다.
응용프로토콜에서의 전송단위를 packet이라고 부른다면,
보통 다음과 같은 형태가 되겠지요.
packet => header + data라고 정의하자.
header => length;type (고정사이즈로 하자)
예 ) packet length : 4 byte ; 4B = 32 bits 따라서, 최대 4G가 가능하겠지만 1024, 4096모 이런정도로 제한을 하자, 플로우 컨트롤을 위해서.또는 거대 (불법,이상한?)패킷을 걸러내는 방법도 되겠지.
packet type : 4바이트
data => header에서 기술한 length만큼이 전달된다고 하자.
대략 프로토콜 포맷의 설계가 끝나면, 플로우를 설계하자.
모든 송신자는 data/control packet을 보내고 ack packet을 수신한다.
모든 수신자는 data/control packet을 받으면 ack packet을 송신한다.
다음으로 패켓을 만드는 함수, 송신하는 함수 수신하는 함수를 제작한다.
이런 방식으로 하면 프로토콜처리에 따른 오버헤드는 있지만, 보다 안전한(?)
클라이언트/서버를 만들 수 있을것입니다.
그리고, setsockopt()로 설정한 것은 당연히 해당 프로그램에만 영향이 미치게 됩니다.
** 질문하신 분의 경우에는 클라이언트 서버를 모두 제작하시는 듯하니, setsockopt()로 시도하시는 것도 방법이고, 자체 플로우 콘트롤을 제작하시는
것도 방법이겠습니다.
------------------ P.S. --------------
지식은 오픈해서 검증받아야 산지식이된다고 동네 아저씨가 그러더라.
[quote="mach"]...대략 프로토콜 포맷의 설계가 끝나면,
mach님 말씀대로, 120K를 1K 씩 나눠보낼 때 1K 보내고 ack 패킷 받도록 바꿔보세요.
올려주신 소스는 1K 씩 나눠보내게는 되어있는데(정확히는 안 봤는데 이 의도로보여집니다.. ^^),
더 중요한 것은 서버쪽에서 1K를 받은 후 ack을 보내주는 것이죠.
이 응용 수준의 ack에 TCP 수준의 ack 이 piggyback되어서 ack이 delay되는 것을 방지하는것이죠.
osanha님 올려주신 링크보니까 재밌는게 많네요.
server쪽의 delayed ack 관련 파라메터 튜닝도 한번 해 보시면 좋을 것같은데요.
delayed ack이 정말 문제인지 확인차 delayed ack을 disable시킨다든지 해서 가능성들을 줄여나가는 것이 좋을 듯 한데요.
제 생각으로는 버퍼 사이즈 조정으로는 좀 한계가 있을 듯한데,
버퍼 사이즈만 조정한다면 해당 소켓으로 한번에 120K가 아닌 1M 쯤을 때리면
delay가 계속 발생하지 않을까 생각이 듭니다.
120K일때는 클라이언트의 sndbuf 에 던지기까지만 하고 작업이 끝나고,
다른 job이 들어올 때까지의 시간간격이 TCP단의 delay보다 더 길다면 문제가 안 생기겠지만,
sndbuf를 꽉 채우고도 수행할 작업(보낼 데이터)이 더 있다면 그때부터는
TCP쪽의 delay가 문제를 일으킬 수 있지 않을까 하는 생각이 들어서요.
부하증가에 따른 accept 시간 지연은 스레드를 따로 분리하는 게 좋을 듯 합니다.
댓글 달기