프로그래밍 언어론 듣고 언어 설계해보고
형식언어, 오토마타론 수업 듣고 언어 체계 정의하는 추상기계 배워보고
컴파일러 듣고 렉서 파서 제너레이터 SSA폼중간언어 최적화루틴들 레지스터할당기 어셈블러도 만들어 보고
솔직히 취향 안맞으면 재미는 없다
근데 대학 나왔으면 컴공 3대장 디비 컴파일러 오에스 이 세 개는 다 들어보고 졸업해야되지 않겠냐?
학부생 때 아니면 이런거 언제해보겠냐
니들이 선택한 컴공이다
할꺼면 한 번 제대로 해보자
참고로 짤은 loop unrolling이라는 최적화 패스를 구현한거다
loop unrolling은 걍 존나쉬운 최적화 방법 중 하나다
그냥 루프를 펼쳐놓은거뿐이다
for i = 0 to 10, i+=1를 i+=1, i+=1 ... i+=1 이렇게 바꾼다.
개나소나 할 수 있을 정도의 최적화 아니겠냐?
근데 왜 루프를 펼쳐놓은게 최적화가 되는걸까? 궁금하지 않냐?
컴파일러를 깊게 배우고나면 저런 최적화기법에 대한 이해 및 구현은 기본이고 언제 저런 최적화를 해야 효율이 좋은지까지
논리적으로 통계적으로 딥러닝모델로 파악할 수 있다
간단하게 예를 들어보자
루프를 왜 펼쳐놓는걸까? 왜 펼치면 좋을걸까?
루프를 펼쳐두면 분기 비용을 줄일 수 있는 효과가 있다
분기 비용이 뭘까?
컴퓨터 구조론 시간에 MIPS 아키텍쳐를 배웠으면
이 아키텍쳐를 배울때 시간 낭비를 줄이기 위해서 명령어 파이프라인를 잘게잘게 쪼개어
각 파이프라인 스텝마다 다음번 명령어를 미리 실행한다는 개념이 생각날 것이다
다음번 명령어를 미리 실행했을때 레지스터의 값이 바뀐다던지하는 문제땜에
현재 파이프라인을 날리고 새로 실행해야되는 것이 비용 문제라고 배웠을 것이다
근데 현대 프로세서들은 모두 견고한 분기예측기를 가지고 있다
대충 이번에 분기를 했으면 다음 번에도 분기를 할 확률이 높으니깐 일단 분기를 미리 실행하고
분기가 실패하면 명령어 파이프라인을 날리고 분기끝나는 부분부터 다시 시작한다
그러면 루프를 풀어놓아봤자 분기 예측보다 못하지 않냐?라고 생각할 수 있다
맞다. 루프 언롤링은 간단한 시그마 연산과 같은 경우를 제외하면 대부분의 경우 분기 예측만큼 좋은 성능을 못 낸다고한다.
왜 그럴까?
루프 언롤링의 개념은 사실 C언어와 같은 고급언어영역에서 볼땐 루프를 펼쳐놓은 것이라고 밖에는 설명할 수 없다
어셈블리, 기계어급의 저급언어관점에서 봤을때도 사실 설명이 힘들다
실제로 프로세서의 적재되어 실행되는 과정을 살펴봐야지 제대로된 설명이 가능하다
리눅스 커널의 배리어 함수를 아는가? 이 함수는 컴파일러가 어셈블리 코드의 순서를 지멋대로 바꾸는걸 막는다
왜 이런 함수가 존재할까? 이런 함수가 존재한다는거는 컴파일러가 지멋대로 어셈블리 코드의 순서를 바꾸기 때문이라는걸 알아야한다
왜 지멋대로 순서를 바꿀까? 이건 어셈블리의 실행순서에따라 명령어 파이프라인을 날려벼려 비용이 발생하는 문제를 막기 때문이다
가령 어셈블리코드가 20줄이 있다. 1줄은 4줄과 연관되어 있어서 1줄이 실행되야 4줄이 제대로 실행될 수 있다.
이 어셈블리코드를 돌리는 프로세서의 명령어 파이프라인이 10줄이라고하자. 한 번에 10개의 명령을 동시에 처리하는거다
맨처음 1번줄이 패치된다. 그 다음으로 1번줄은 디코딩되고, 2번줄이 패치된다 쭉쭉 따라가보자
근데 문제가 생긴다. 1줄과 4줄이 연관되어있기때문에 4번줄이 실행되고나서 1번줄이 실행되어야함으로 4번줄의 실행이 끝나면
파이프라인을 날려버려야한다. 즉, 비용이 발생한다.
컴파일러는 이 비용을 최소화하기 위해 기존 1줄과 4줄을 멀리 띄어놓아서 파이프라인이 stall되는것을 막는다
루프 언롤링을 다시 살펴보자. 루프 언롤링은 단순히 루프를 풀어헤친다고하지 않았나?
단순하게 1을 계속 더하는 루프를 살펴보자. for i = 0 to 10, i+=1이다.
이걸 풀어 헤치면 i+=1, i+=1 ... i+=1이 된다. 그렇다면 위에서 말한대로 stall문제때문에 비용이 비쌀까?
그건 아니다. 이정도는 명령어 파이프라인이 stall하진 않는다. 그냥 풀어헤치던 말던 좆대로해도 큰차이는 없다
그렇다면 다른 루프는 어떨까? 가령 a=[1,2,3,4,5,6,7],x=0,foreach e in a, x+= e이 있다고 해보자
위 루프와 다른 점은 배열이 들어갔다는 점이다. 이걸 풀면 x += a[0], x+=a[1], ... x+=a[6]이 될 것이다.
이건 어떨까? 이것도 사실 stall 문제는 없다. 명령어간 의존성이 그다지 크지 않기 때문이다
다만, 이 코드는 루프 언롤링했을때 대부분의 아키텍쳐에서 더 빠르게 실행될 여지가 있다
캐시라인, 캐시히트의 개념을 알고 있다면 이해가 쉬울 것이다.
루프를 풀기전 코드는 a의 iteration에 사용되는 코드와, a를 참조하는 코드 두 가지를 주목할 필요가있다
a의 iteration하는 코드와 a를 참조하는 코드 두 가지가 동시에 같은 횟수만큼 실행되기 때문에 작업이 균등하게 배분되지 않는 다는 문제가 있다
루프를 풀어헤친코드는 a를 iteration하는 코드가 없기 때문에 a를 참조하는 코드 부분만 집중할 수 있다.
이는 단순한 예제지만 조금만 확장해서 생각하면 루프 언롤링이 캐시히트율에 미치는 영향이 커지는 것을 알 수 있을 것이다.
그렇다고 무작정 풀어헤치면 안된다. 캐시라인이 캐시히트율에 영향을 미치기 때문이다.
또, 캐시라인, 캐시히트율을 고려하지 않아도 루프 본문의 크기가 크다면 명령어 바이트를
캐싱해놓는 부분도 캐시 미스가 발생하는 확률이 커지기 때문에 그러면 안된다
그러면 stall문제는 언제 발생한다는 것인가?
간단한 예를 또 들어보자. a=new int[100], a[0] = 1, for int i = 1 to 99, a[i] = a[i-1] + 1
이걸 풀어놓으면 a[1]=a[0]+1, a[2]=a[1]+1 ... a[99]=a[98]+1이 된다.
이 명령들이 명령어 파이프라인에 들어간다면 한 스텝 건너 stall이 발생한다. 왜냐하면 a[2]를 계산하는데에 a[1]의 값이 필요하고,
a[3]를 계산하는데 a[2]의 계산이 필요하고 이런식으로 연속된 체인 형식으로 참조가 발생하기 때문이다.
그러면 다음 코드를 보자. a=new int[100], b=new int[100], for int i = 1 to 99, a[i]=a[i-1]+1, b[i]=b[i-1]+1
이걸 풀어 놓으면 a[1]=a[0]+1,b[1]=b[0]+1 그다음 a[2]=a[1]+1,b[2]=b[1]+1 ... 이된다.
위 예랑 다른 점은 a에 대한 참조 바로 다음스텝에 일어나지 않는 점에 있다
즉 이전 예제는 한 스텝 건너 stall이 발생한다면 이 예제는 두 스텝 건너 stall이 발생한다. 대충 두 배 정도 성능이 좋아진것 같지 않나?
배열을 또 추가하거나 stall이 발생하지 않는 다른 연산부분으로 코드를 채워둔다면 루프 언롤링은 좋은 성능을 보일 것이다
컴파일러에 수 많은 최적화 기법 중에 딱 한 가지만 가져온 것이다.
더 많은 것을 배우고 익혀 볼 것을 생각하니 벌써부터 재밌지않나?
컴파일러 수업을 듣고 더 많은 최적화 기법들을 알아보자.
어쩌라고
엉덩마타
와 씨발 ㅋㅋㅋ 복붙이지? 복붙아닌데 저렇게 써놓은거면 여기서 저거 읽을 애들 몇이나 될까 씨발 ㅋㅋㅋ
바로 스크롤 죽 내렸네 씨발 ㅋㅋㅋ
30분동안쓴거야 ㅠㅠ
좋은 내용이라고 생각하니깐 읽어봐
장황하게 쓴글은 개 ㅈ같이 못쓰는 글이다 문맥 문장 고려안하고 똥싸갈기듯이 쓴글은 글이 아니다
중간부터 이해 못 해서 내렸지만 재밌게 읽었음 근데 컴파일러를 우리같은 엔드단의 유저가 최적화 해야 될 이유가 뭐가 있음?? 왜 그런 식으로 미리 컴파일러를 짜놓지 않는거야?
컨텍스트도 좀 나누고 가독성만 높이고 단어들 설명만 좀 해줬어도 훨씬 괜찮은 글이 되었을듯
바꿈
컴파일러는 알아서 코드를 최적화해주지만 그런 최적화를 하면 안되는 경우가 생겨. 프로그램을 컴파일하고 실행하는데 문제가 발생하는데 디버깅할 때는 문제가 발생안해. 이러면 컴파일러 최적화때문에 문제가 발생했을 가능성이 높지. 이런 경우에 문제가되는 최적화 부분을 제거를 해줘야하잖아? 게임엔진같은거 직접 만드는 회사들은 그런거 찾으려고 컴파일러 개발자들한테 문의 많이 하더라구.
특히 float나 double같은 고정/부동 소수점 연산들은 연산의 순서가 매우매우 중요해. 특히 의학쪽이나 우주개발쪽에선 아주 민감하다고하네. 이런 문제들 때문에 의학이나 우주개발쪽은 컴파일러를 통채로 개조한다고 하더라구
스크립트 언어들 jit 같은 경우에도 적용됨ㅇㅅㅇ
아 결국엔 컴파일러도 최소한의 비용으로 최대한의 이득을 뽑아내려고 하다보니 대부분의 경우에는 최적화가 잘 되어있지만 바운더리쪽에 갈수록 최적화가 안 된다는거구나 이해했음 나도 그런 도메인 가게 되면 공부할게
qtzz
나만의 생각인데 한국기준 컴파일러 만들려면 서울대, 카이스트 컴공과 졸업장으로도 부족하다.ㅇㅇ 서울대, 카이스트 들어가서 학부생때 1,2년 유학갔다오고 학사 졸업한 다음 대학원은 MIT대학원, 캘리포니아 공대, 하버드나 버지니아 공대 컴공과 에서 박사학위를 받고 IBM컴퓨터 연구소, 구글 본사 연구소, 애플 본사 연구소에서 15년 이상 컴퓨터 공학으로 연구를 해봐야 남들이 인정할 만한 제대로된 컴파일러 하나 나옴ㅇㅇ 현재 책 제목이 컴파일러 만들기 라고 되어 있는 책들은 컴파일러 교육용이지 진정한 컴파일러 개발에는 발도 안 들인 것임.ㅇㅇ
컴파일러 교재 추천점 해주세여 - 2400
저 과외좀 해주세요... - 2400
나이 30세 넘은 비전공 또는 고졸이면 이미 늦었음.ㅇㅇ 일반 국립대 컴공과에서 데이터베이스, 컴파일러에 제대로 뒷통수 맞아봐야 하드웨어 개발, 소프트웨어 개발의 특징을 알 수 있고 나중에 하드웨어 개발 국비를 어이없게 다니게 되어도 난 소프트웨어 개발을 중점적으로 공부해서 자바스크립트, 스프링에는 자신 있는데 하드웨어 개발에서 공부하는 arm회로 개발, 임베디드 프로그래밍에 완전 젬병이라고 자신 있게 말할 수 있는 거 임.ㅇㅇ
29살 비전공이니까 안 늦었죠? 가르쳐 주세요 - 2400
과외좀해달라는데 지혼자 30세가 넘었니 비전공이니 고졸이니 별 딴소리를 장황하게하노 ㅋㅋ
몇줄 읽다가 바로내림
csapp책에 나오는거네
해당 댓글은 삭제되었습니다.
https://github.com/llvm/llvm-project/blob/main/llvm/lib/Transforms/Utils/LoopUnroll.cpp#L269
흥미롭다
팩트 ) 지잡은 오토마타 안배움 ㅋㅋ
어떻게든 시간표 짜서 들을걸
응 존나재미없더라
코로모님 말씀이 백만번 옳습니다. 개추 드림.
어차피 si 스타트업 3교대 2400 인생인데 뭐하러 컴파일러 쳐 배우노? 그시간에 주식이나 배워라 ㅄ들 ㅋㅋ
최적화 안할랍니다 ㅋㅋ
진짜 이 형 밑에서 같이 일하고싶다 ㅠㅠ 2400받아도 좋으니 써주세요
저 무급백수아싸히키예요 ㅠㅠ
@코로모 무급백수아싸히키가 이 정도의 공부를?
공부를 대체 얼마나 많이해야 이정도 식견이 생길가 물론 글은 안 읽었지만 모르는 내용이라 어쩔수 없음 ㅠ
잘읽었읍니다,, 글솜씨가,,뛰어나시군요,,^^
너 내밑으로 와라 ㅇㅅㅇ
이런건 쓰레기통이 아니라 네이버 블로그에 써주세요 코로모씨
아니 loop-unroll이랑 파이프라인 모르는사람이 어렇게 많음?
3머장은 OS, DB, NW인데
이정도면 학부2학년지식으로도 거의다 이해할수있지않나? 프갤수준 개엠창이네
이러니 알고리즘 같은 게 필요없다는 소리가 나오지 ㅋㅋ
뭔 쌉소리 하는지 모르겠는데 컴파일러론 ㄱ어려워서 똥쌌다. 과제도 ㅈㄴ어려워
사실 저건 c언어만 공부해도 알게 됨 루프 언롤링이라는게 사실 int i; for(i=0;i<=n;i++) 이거랑 같은 의미임
겠냐?
스위치 케이스 문이 점프 테이블로 치환되는 방식좀 설명좀요.. 구현하는 중인데 감이 안와서요
유익한 글인데 댓글은 씹창나잇네ㅋㅋ;
컴공 3대장은 운체 컴구 컴파일러지
ㅈㄴ재밌네 컴파일러론 선택과목이라 딴거들었었는데 좀 후회되기도하고