프로그래머의 강박증

slee0303의 이미지

for loop를 사용할 때,

for(int i = 0; i != 100; ++i)
...

위 코드처럼 쓰는게 빠르다고 하는데(지금도 그런지는 모르겠지만, 기계어에서 jump not equal 이 jump less than 보다 빠르다고 함),
자꾸 아래처럼 쓰게 되네요.

for(int i = 0; i < 100; ++i)
...

위 코드에서는 기계가 루프의 상한을 놓치면 무한 루프를 돌거나 메모리 침입 에러가 나거든요.

Rubypops의 이미지

아래 코드는 프로그래밍 언어책의 전형적 코드.

ㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋ 저도 무의식에 아래 코드를 많이 씁니다.

틀에 벗어난 프로그래밍 해야 할텐데요.

무의식이란 참 무섭 습니다.

루비를 공부하고 사랑하는 프로그래머

pjh0431의 이미지

그럼 위에 코드가 더 좋다는 말인가요? 그럼 저도 위처럼 점점 바꿔서 코딩해야겠네영

thinxs의 이미지

음, 10%는 맞고 90%는 틀린 것 같습니다.

for(int i = 0; i != 100; ++i)
...

만약 이 루프 안에서 i가 99 -> 101로 설정되었다면 i < 100으로 비교했을 때에는 정상적으로 루프가 종료되는데, i != 100으로 비교하면 루프를 탈출하기 어려워지죠. 그리고 요새의 컴파일러와 CPU를 너무 얕잡아볼 필요도 없습니다.

아, C++에서 iterator를 쓴다면 이야기가 조금 달라지긴 하지만, C++11의 foreach를 쓴다면 iterator를 굳이 for()에다가 끼워넣을 필요도 없습니다. 할렐루야!

DarkSide의 이미지

뭐, 위 코드에서 루프가 잘못될 일은 없습니다.
그럼에도 신경이 쓰이니 강박증이라고 하신 거겠죠.

그리고 for 루프 내에서 인덱스가 쓰이는 일도 많기 때문에, 언제나 foreach를 쓸 수 있는 건 아닙니다.

라스코니의 이미지

for 루프 인덱스가 많은 경우에 배열 인덱스로도 사용되기 때문에 배열이 0부터 시작하는 C 언어에서는 for (i=0;...) 이 사용되는 경우가 많겠죠.

jick의 이미지

그냥 도시전설이거나, 특정 버전의 CPU에서만 들어맞는 얘기 아닌가요?

x86에 대해서는 인터넷을 뒤져보니 이런 테이블이 나오는데 "conditional jump"로 찾아보시면 JNE, JL 등등을 구분하지 않고 다 같은 latency를 보여주고 있습니다.

http://www.agner.org/optimize/instruction_tables.pdf

HDNua의 이미지

1. 컴공 학부생의 수준 낮은 생각임을 미리 밝힙니다.
제 생각이 옳다고 주장하려는 것보다, 제가 아직 많이 모른다는 점을 느끼고 싶어서 덧글 남겨봅니다.

!=로 비교 연산을 하는 것은, less than 연산보다 훨씬 빠릅니다. CPU가 Pipelined Architecture라면요.

아주 기본적인 CPU에서는 branch 연산은 EX stage에서 ALU가 계산한 후, MEM 단계에서 PC를 업데이트합니다.
하지만 Pipelined CPU Architecture에서 branch 연산은, ID 단계에 비교 모듈(xor)을 넣는 것으로 ID 단계에서 바로 branch가 일어나도록 할 수 있습니다.
하지만 less than 연산은 반드시 ALU가 계산하여 결과를 내놓아야 하기 때문에, ID 단계에서 branch를 할 수 있는 데에 비해 2개의 stall이 생기는 것과 같죠.

컴파일러가 똑똑해서 루프 내에서 i에 대해 less than 연산을 not equal로 바꾼다면 latency는 같아집니다.

덧. x86과 같은 CPU는 옛날에는 CISC 계열의 CPU였지만, 요새는 Instruction만 CISC고 실제 구현은 RISC로 바뀌었다고 알고 있습니다.
(수업 시간에 교수님한테 그렇게 들었던 기억이 나는데, 근거를 찾아오라시면 시간이 걸릴 것 같습니다. 잘못 들었을 수도 있구요.)

Pipelined CPU의 사진 첨부합니다. 빨간색 줄은 equal로 연산했을 때, 주황색은 ALU가 연산하고 MEM에서 PC로 넘길 때를 표현해보았습니다.

내공이 부족합니다. 많은 가르침 부탁합니다.

출처: 학교 수업 with "Computer Organization and Design - the Hardware/Software Interface 5th"

댓글 첨부 파일: 
첨부파일 크기
Image icon Cap 2016-06-12 01-46-51-758.png67.25 KB

저는 이렇게 생각했습니다.

세벌의 이미지

유지보수를 생각하면 전자보다 후자가 좋아 보입니다.

cats96의 이미지

이거는 속도가 어떨까요?

for(;;)
{
if(i++ == 100)
break;
}

세벌의 이미지

i 초기값을 지정해주지 않으면 원치 않는 결과가 나오겠네요.

koreaphb의 이미지

제가 생각할때는 좋은 프로그램 = 제대로 동작하면서, 가장 유지보수하기 좋은(읽기 좋은) 프로그램이라는 측면에서

전자는 의미가 불충분한 코드고 후자는 의미가 완벽한 코드가 되겠죠.

물론 리얼타임 OS 라던지, 퍼포먼스가 안정성보다 훨씬 중요한 SW에서는 비트연산이라던지 가능한 최적화를 해주는게 좋을테지만...

그럼 어셈블리를 사용하는게 더 좋지 않을까요? 그렇게 퍼포먼스 디펜던트하다면...

pinebud의 이미지

기계어 전문가가 오시면 좋겠지만
현대 CPU에서는 < 이나 !=이나 한 인스트럭션 아닌가요?
Branching cost가 다른지는 모르겠습니다만.

또한가지 고려할 사항은 아래와 같은 케이스도 있지 않나요?
나쁜 코드기는 하지만요..

for (int i = 0; i != 100; i++) {
if (something) i += 2;
}

A rose is a rose is a rose..

DarkSide의 이미지

루프 내에서 인덱스 자체를 변화시키는 행위를 해서는 안됩니다.
다른 방법을 찾아야죠.

HDNua의 이미지

질문입니다.
왜 루프 내에서 인덱스 값을 바꾸는 행위를 하면 안 되나요?

저는 이렇게 생각했습니다.

thinxs의 이미지

프로그래머가 의도하지 않은 행위로 루프 인덱스가 변화될 수도 있습니다. 뭐 그냥 숫자 합만 구하는 거라면 몰라도 거기 안에 데이터가 들어간다면 상황이 달라지죠.

viper9의 이미지

저도 후자가 낫다고 생각하는데요.

쓰던 문법에서 익숙해서 그런건지 아랫것이 더 안전하겠다는 생각이 듭니다. 위에코드는 의미가 한번에 잘 이해되질 않네요..;;

그나저나 저정도는 컴파일러가 최적화 해주지 않을까 싶은데요.

만약에 저 두코드의 성능 차이로 문제가 생기는 프로그램이라면 저라면 그냥 루프 안 쓰고 함수 호출을 100번 copy&paste 할것 같네요. :)