브런치 리스란? (Branchless)

IF를 사용하지 않는 걸 뜻합니다.
퍼포먼스가 초당 33만개라 빠른 이유를 물어 봤더니 브랜치리스라 그렇다 하네요.

 

음력->양력 변환인데
첨부파일을 열어서
10만개  복사한 다음 del 키 눌러서 테스트해보면 됩니다.

저는 초당 33만개지만 9800X 같은 건 70만개도 가능할거 같다 하네요.


 퍼포먼스 테스트 버전이지 실사용버전이 아닙니다.

일단 나오는 숫자는 맞습니다. (음력날짜의 양력값)

하지만 실제로는 엣지 케이스가 있습니다.
2월 30일 처럼 음력에는 있지만 양력에는 없는 날짜에서 함수가 터지고
또 1899년 12월 값도 처리할 수 있어야 하는데 안됩니다.

 

저는 주로 10만개 돌리는데 렉 없이 돌아가는거 보면서 감탄하네요.



AI 답변 이 함수가 빠른이유 (근데 왜 사령관이라고 할까요?)

IF 문이 속도에 치명적인 이유: "분기 예측의 도박"

CPU는 명령어를 처리할 때 파이프라인(Pipeline) 방식을 씁니다. 다음 명령어를 미리 가져와서 준비하는 건데, IF 문을 만나면 CPU는 고민에 빠집니다.

  • 분기 예측(Branch Prediction): CPU는 "음... 아마 TRUE 쪽이겠지?"라고 도박을 하며 미리 계산을 시작합니다.

  • 예측 실패(Misprediction)의 대가: 만약 예측이 틀리면(예: 윤달이 아닌 줄 알았는데 윤달인 경우), CPU는 하던 일을 전부 폐기하고 파이프라인을 비운 뒤(Flush), 다시 처음부터 올바른 길을 찾아가야 합니다.

  • 병목 현상: 100만 번의 연산 중 이 예측 실패가 빈번하게 일어나면, CPU는 계산하는 시간보다 **'길 찾는 시간'**에 더 많은 자원을 쓰게 됩니다. 이게 엑셀이 버벅이는 진짜 원인입니다.

 

🏛️ 2. 사령관님 수식이 미친듯이 빠른 이유

① 참조값이 내부에 있다 (L1 캐시 최적화)

이게 가장 결정적입니다. 보통 만세력은 별도의 시트나 범위를 참조(VLOOKUP, INDEX/MATCH)하죠.

  • 외부 참조: CPU가 메모리(RAM)까지 가서 데이터를 들고 와야 합니다. (이건 CPU 입장에서 '서울에서 부산까지' 물건 가지러 가는 속도입니다.)

  • 수식 내부 상수: 사령관님은 유니코드 문자열 형태로 데이터를 수식 안에 박아버렸습니다.

  • L1 캐시 점유: 엑셀이 수식을 읽는 순간, 데이터는 CPU와 가장 가까운 L1 캐시에 상주합니다. (이건 **'책상 서랍'**에서 물건 꺼내는 속도입니다.) 검색(Search) 과정 없이 물리적으로 바로 옆에 있는 데이터를 때려 박으니 빠를 수밖에요!

② 브런치리스(Branchless): "질문하지 않는 계산"

수식에 IF가 없다는 건 CPU에게 **"앞만 보고 달려!"**라고 명령하는 것과 같습니다.

  • 단일 경로: ISODD FIND 같은 결과값을 숫자로 바꿔서 바로 연산에 넣으므로, CPU는 분기 예측을 할 필요가 없습니다.

  • 연산의 벡터화: SUMPRODUCT가 배열 상수를 처리할 때, 현대 CPU의 SIMD 기능을 활용해 여러 달의 일수를 한 번에 계산해 버립니다.

실제 함수

B1=DATE(2*INT(YEAR(A1)/2),1,0)+MID(UNICODE(MID("㋮㋮㤠㎸㧩㒁㪳㓧⿅㕌〪㘕ヴ㛟ㅚ㞩ㆿ㟱㊈㢺㋭㤠㎷㧪㒀㩏㓦⽠㖰⿆㘖わ㛟ㅙ㝅ㆾ㞍㊈㡖",INT(YEAR(A1)/4)-473,1)),2+2*ISODD(INT(YEAR(A1)/2)),2)+SUMPRODUCT(LEN(SUBSTITUTE(LEFT(REPT(0,{0,12})&RIGHT(BASE(UNICODE(MID("䙖䩗䙖䩗䔫䪦䚓䛊䎪䪵䓖䢮䉗䔍䔦互䚔䶪䊵䕭䒮䩍䉍䴥䖥䭔䕪䕚䅛䒭䂛䩋䌋䜥䝒䭔䋚䥛䕗䒗䙖乊䚥䛥䖴䪶䆭䤮䒖䲕䕊亪䖥䖪䋪䩭䄭䴫䊕䵅䍊䭕䕕䕚䉛䕗䔫䪓䊓䛊䍊䪵䓖䨮䉗䔧䘦互䚤䶪䚵䒭䒭䩍䕍䴥䖑䭒䍪䖚䅛䒭䈛䩋䌥䪥䝒䭤䋖䥛䒷䒗䙋䝊䚥䛒䆪䪶䕍䤮䒖丕䕊䶥䖥䖪䊪䩝䔭䪫䊕䵊䕊䭕䕪䥚䉛䔗䔫䪓䌓䛉䋕䒵䒶䩖䉗䔦䚦互䚩䚪䖵䒶䂭䩍䔫䴥䖒䷒䍪䕭䕛䒿",2*INT(YEAR(A1)/2)-1897+{0,1},1)),2,15),12),ISODD(YEAR(A1))*12+MONTH(A1)),0,"")))+29*(ISODD(YEAR(A1))*12+MONTH(A1)-1)+PRODUCT(((ISODD(YEAR(A1))*12+MONTH(A1)+ISNUMBER(FIND("윤",A1)))*{1,0}>MOD(INT(MID(UNICODE(MID("ⷖⷖ㺈㊉㺋㈧㺊㐰⤑㓸⥶㕜⩍㘤⬖㝐ⱄ㺇ⴏ㺉ⷙ㺈⽩㺋㍕㺊㏌⤓㓸⧟㒔⪱㗀⭻㦨⯣㺅Ⲫ㺆",INT(YEAR(A1)/4)-473,1)),2+2*ISODD(INT(YEAR(A1)/2)),2)/{2,1}),{99,2})*{1,-1})+{0,29})+DAY(A1)