PHP의 개안습 같은 속도와 극히 대조적인 C언어의 속도(?)
112만 7603 레코드, 10개 필드, 93개 데이터베이스에 해당하는 규모의 데이터 (50MB 가량)를 읽어서 메모리에 적재하고, 그걸 또 계산할 일이 있어서 프로그램을 작성했다.
데이터 출처 자체가 온라인상이라서 PHP를 사용하여 해당 웹 페이지를 긁어와서 저장했고, 계산까지도 PHP를 사용했다. 그리고 이 프로그램에 먼지가 쌓이도록 내버려뒀었다. 거의 1년간.
이제 와서, 본격적으로 뭔가를 해보려는데, 뭘 해보려 해도 파일을 읽고 쓰는 속도가 너무 느려서, 속도를 개선할 필요를 느끼게 되었다.
PHP에서는 마땅히 좋은 방법이 없는 것 같아서, C언어로 짜보기로 했다. 그래서, 얼마나 빨라졌나 볼까?.
첫 번째 테스트 (PHP 사용) : 1.8초
Plain Text 읽어오기, fscanf, fgets 사용
두 번째 테스트 (C언어 사용) : 2초~3초
Plain Text 읽어오기, fseek(파일 한꺼번에 버퍼에 읽어오고 버퍼내용 일일이 훑으며 메모리에 적재), sscanf, atof 사용
오버헤드를 유발하는 비효율적인 함수(sscanf , atof) 같은 거 쓰니까 PHP와 별 속도차이 없거나 오히려 느림
세 번째 테스트 (C언어 사용) : 0.4초
Plain Text 읽어오기, fseek(파일 한꺼번에 버퍼에 읽어오고 버퍼내용 일일이 훑으며 메모리에 적재), 약간의 하드코딩
포인터를 이용하지만, 여전히 float 소수점을 일일이 변환하는 과정에서 문제 발생. - 여전히 오버헤드 있음.
while(*p!='\0'){
'\t'가 나올때마다 ~ 지금까지 버퍼에 저장된 문자열을 일일이 char-'0' 하고 각 자릿수에 해당하는 자릿수를 일일이 계산해서 곱하고 ~~ 블라블라
}
이 과정 자체도 오버헤드 있음.
그리고 PHP에 비해 그렇게 압도적으로 빠르지 못함.
저 속도는 한때 나의 희망이었던 C언어의 속도일 수가 없어!
그래서 메모리의 내용을 파일에 그대로 저장하는 방법이 없나 검색을 해봤다.
대충 보니까 바이너리 읽기모드라는게 있더만?
그래서 Plain Text를 바이너리 파일로 바꾸려고 별별 걸 다 실험해봤다.
제일 처음에, 데이터베이스에 대한 메타정보가 담긴 파일을 바이너리로 작성해봤는데, 아주 잘 되어서 만족스러웠다.
그래서, 그 다음에 모든 데이터를 바이너리 파일로 바꾸려 했다. 음, 여기엔 문제가 하나 있는데 모든 데이터를 새로 받아올 생각을 하니 막막해졌다. 1시간 정도 집중하면 다 받을 수 있겠지? 사실 나는 이런 종류의 작업이 모두 자동화되도록 머리를 쓰기 싫어한다. 뭔가가 막 복잡해지는 것 같고.
뭐 내 실력이 여기 까지겠지.
솔직히 로직은 전부 함수별로 분리되어 있어서 그냥 for 문 쓰면 장땡인데...
거기다 데이터가 어디서 시작하는지에 대한 메타정보까지도 있겠다, 시간 예측까지 다 할 수 있잖아? 그런데 난 그렇게 안 했다. 왜냐하면 귀찮으니까.
그냥 지금 받아놓은 데이터를 바이너리파일로 변환하기로 했다.
이제 와서 생각해보면 그 때 그냥 그냥 C언어로 바이너리파일을 만들 걸 그랬나 하는 생각이 든다.
아무튼 간에 PHP의 pack함수로 바이너리 파일을 작성했다. 시간이 꽤 걸리더라. 그래도 30초 안에는 마치더라.
두둥! 그렇게 해서 바이너리 파일을 읽기...
네 번째 테스트 (C언어 사용) : 0.7초
Binary 읽어오기, fread(&변수,sizeof(형식),1,fp)
아놔! 워째서 바이너리 읽기가 Plain Text 읽어오기보다 느린 거야? 이건 말도 안돼!
이럴 수는 없어!
내가 프로그램을 어떻게 짰냐면, 레코드가 112만개인데, 거기에 10개나 되는 필드를 일일이 1개씩 읽어오니까 느려질 수밖에 없다. 웬만한 연산도 1120만개 정도 되는 데이터를 일일이 읽어오면 그만큼 느려진다. 게다가, fread 함수 자체도 1120만번인가 불려왔으니까 느려질 수밖에 없지!?
거기다 하드디스크 I/O도 있지? 이건 윈도7의 경우는 메모리가 웬만큼 버퍼해주는거 같으니까 상관없을테고...
아무튼 그래서 바이너리 파일의 구조를 변경할 수밖에 없었다.
다섯 번째 테스트 (C언어 사용) : 0.026초 !!!!!!!
Binary 읽어오기, fread(&변수,sizeof(형식,recN,fp);
와우! 이건 내가 기대했던 대로의 성능인데?
소스코드도 엄청나게 심플해져서 좋았다.
아무튼 이런 아주 간단한 일을 낑낑대면서 하는 걸 보면 나는 고급 프로그래머 축에는 절대 못 낀다.
아무튼 파일 입출력문제를 해결해서 기분 좋다.
예전에 포기한 OpenCL도 다시 한 번 파볼까나.... 자자자자자자자자자 ㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋ
정정
이제, 보니까 검색 조건을 걸어놨음. -_-;;
1번 항목은 3.3초 소요된 것으로 정정함.
C언어처럼 구조체에 적재하고 사용성까지 똑같게 하면 PHP의 속도는 4배 더 느려질 것.
최종적으로 150배~600배 성능 차이인데... 헐...
검색조건만 잘못된 게 아니고, 잡다한 코드도 있어서 그것 제거하고 동일한 조건으로 세팅. (메모리 적재되는 시간만 측정)
이건 그거고, PHP도 바이너리 읽기가 그렇게 비효율적이지만은 않다는 것을 알 수 있었음. Plain Text 읽기와 비슷한 시간 소요
용도에 맞게 사용하시면 되겠죠. 0.027초 걸리는
용도에 맞게 사용하시면 되겠죠.
0.027초 걸리는 것과 3.3초 차이가 나는 것은 엄청난 차이지만, 0.027초 걸리게 짜는 것 보다 3.3초에 처리하도록 하는 것이 총 소요시간이 떠 짧다면 0.027초 걸리는 것이 과연 좋은 것일까 생각해보실 필요는 있을 것 같습니다.
그리고, php도 c로 고민하는 만큼 하시면 0.027초는 몰라도 3.3초 보다는 훨씬 짧은 시간내에 하실 수 있을 겁니다. c로 구현하는 방식대로 php로 구현을 한다는 비교 자체가 php의 언어 특성을 전혀 고민하지 않으셨다는 판단이 가능할 것 같습니다. 언어의 특성을 알고나면 그만큼 더 최적화가 가능하니까요.
안 그래도...
데이터를 받아올 때 C언어로 날코딩을 할 생각하면 끔찍함.
데이터 받아오는건 PHP나 기타 스크립트 언어를 쓰는게 맞겠죠. 빠른 시간내에 데이터 추출이 가능하니깐.
일단 제가 작성한 C프로그램은 아주 원시적인 형태의 DBMS라고 생각함. 몇 가지 알고리즘과 자료구조에 최적화시키면 모르긴 몰라도 상용 DBMS에 뒤지지 않는 성능을 내줄 듯. 오히려 특정 목적으로만 최적화되어 있으니 훨씬 더 빠른 성능을 내줄 듯. SQL 쿼리 오버헤드도 제거 가능할 것 같고. 안정성과 범용성은 ? ㅋㅋㅋㅋㅋ
서버는 Apache 서버, HTML 문서 작성은 PHP, 데이터베이스는 내가 C로 작성한 DBMS? ㅋㅋㅋㅋㅋ
필요하면 좀 더 연구해서 HTML 문서 자체도 연결 리스트 같은 걸로 메모리에 띄워놓고, PHP는 개무시, 아파치서버의 FASTCGI 모듈과 연계시키던지 ㅋㅋㅋㅋㅋㅋㅋㅋ 그럼 활용 가능한 기능은 더욱 감소 ㅋㅋㅋㅋㅋ PHP라는 최고의 라이브러리를 내치고 맨땅에 헤딩하는 격 ㅋㅋㅋㅋㅋ
근데 제가 이걸 개발할 능력이 없다는게 함정. System 내부에서 프로그램 간에 메시지를 효율적으로 교환하는 방법도 모르는 마당에 ㅋㅋㅋ
MYSQL은 3306번 포트? TCP인가 UDP인가 사용하는 것 같고...
함정치곤 굉장하네요.
0.026초라... 제가 6.5억 라인 바이너리로 데이터 추출해서 소트해서 랭킹집계하는걸 만들었는데 2011MBA에서 58초 걸리더라구요 ㅋㅋㅋㅋㅋㅋㅋ 함정이라도 부럽네요 ;)