2012-03-29 5 views
6

간단한 에뮬레이터를 작성 중이며 (해야합니까? 아니면 다시 C로 가야합니까?). 어쨌든 , 나는 명령을 가져오고 그것을 해독 중입니다. 이 시점에서 나는 0x81과 같은 바이트를 가지고 있고, 나는 올바른 함수를 실행해야한다.함수 표와 golang 스위치

내가 이런이

func (sys *cpu) eval() { 
    switch opcode { 
    case 0x80: 
     sys.add(sys.b) 
    case 0x81: 
     sys.add(sys.c) 
    etc 
    } 
} 

이나 뭐 같은 것을

var fnTable = []func(*cpu) { 
    0x80: func(sys *cpu) { 
     sys.add(sys.b) 
    }, 
    0x81: func(sys *cpu) { 
     sys.add(sys.c) 
    } 
} 
func (sys *cpu) eval() { 
    return fnTable[opcode](sys) 
} 

1.which 하나가 낫다해야합니까?
2. 어느 것이 더 빠릅니까?

3. 함수를 인라인으로 선언 할 수 있습니까?
4.i에는 레지스터가있는 cpustruct이 있습니다. 레지스터가 있고 전역 변수가 있으면 더 빠릅니까? (struct 제외)

대단히 감사합니다.

+0

만약 컴파일러가 이런 식으로 점프 테이블을 만들지 않았다면 놀라실 것입니다. 특히'opcode'가 바이트 인 경우에 말이죠. 나는 당신의 벤치 마크 결과를보고 싶다. –

+0

다음 질문에 대한 메모 : 귀하의 질문 4는 귀하의 * 일반적인 질문에 대한 주제가 아닙니다. IMO는 질문에 일반적인 질문/주제의 측면에 대한 몇 가지 질문을 포함 할 수 있지만, 주제와는 다른 질문은 별도의 질문으로 요청해야합니다. – Kissaki

+0

의심스러운 경우 어셈블리 출력을 확인하십시오. –

답변

2
  1. 첫 번째 버전은 나에게 더 잘 어울립니다. YMMV.

  2. 벤치 마크 그것입니다. 최적화 할 때 컴파일러가 얼마나 좋은가에 달려 있습니다. 컴파일러가 최적화하기에 충분히 열심히 노력하지 않으면 "점프 테이블"버전이 더 빠를 수도 있습니다.

  3. "인라인 함수 선언"의 정의에 따라 다릅니다. Go는 최상위 레벨에서만 함수/메소드를 선언하고 정의 할 수 있습니다. 하지만 함수는 Go의 일류 시민이므로 변수/매개 변수/반환 값과 함수 유형의 구조화 된 유형을 가질 수 있습니다. 이 모든 곳에서 function literal은 변수/필드/요소에 할당 될 수 있습니다 ...

  4. 가능합니다. 여전히 전역 변수에 cpu 상태를 유지하지 않을 것을 제안합니다. 당신이 가능 멀티 코어를 모방 가기로 결정되면, 내가 몇 가지 벤치 마크하고 약 사가지 경우보다 더 일단 테이블 버전이 스위치 버전보다 빠른했다 ;-)

+0

답에 감사드립니다. 나는 80과 81의 10000 연산을 가진 테스트 입력 파일을 만들어서 말하고 시간을 주어야합니까? 또는 거기에 더 많은 opcode가 있어야합니까? 나는 그런 종류의 벤치마킹을 할 수있다. 인라인 일에 대해, 나는 C의 인라인 함수를 생각하고 있었고, 그래서 함수의 코드는 사용 된 장소에 통합 될 수있다. 내가 할 수 있을까? 그래서 나는 구조체에서 지금처럼 상태를 유지해야합니다. 일부 편의를 위해 약간의 속도를 희생합니다. 마지막으로, 계속해서해야합니까? 아니면 C에서해야합니까? 나는 새로운 시스템 랭 (lang)으로서 학습을 배우고 있으며, 그것이 내가 그것을 시작한 이유이다. – pvinis

+1

- 컴파일러는 연속적인 값 범위, 모든 값 (바이트 표현식의 경우 256)을 포함하는 범위를 넘나들기 위해 다른 코드를 생성 할 수 있습니다. 가장 좋은 것은 컴파일러 내부를 모르는 경우 실험하는 것입니다. - 인라인은 컴파일러의 기준에 맞는 함수를 Go에서 자동으로 수행합니다 (small/short 또는 return expr 문만을 사용하는 등). 에뮬레이터는 C에서 좀 더 빠를 것입니다 (예 : 배열 범위 검사). 하지만 Go에서 더 빨리/더 쉽게 작업 할 수 있다고 생각합니다. 나는 Go를 선택 하겠지만 최소한 새로운 것을 배울 것입니다 ;-) – zzzz

+0

cool. 감사!!! – pvinis

15

환영합니다.

Go 컴파일러 (gc, 어쨌든, gccgo에 대해 확실하지 않음)가 조밀 한 스위치를 점프 테이블로 전환 할 정도로 똑똑하지 않은 것으로 보이는 것에 놀랐습니다.

업데이트 : 켄 톰슨은 the difficulties of optimizing switch을 설명하는 이동 메일 링리스트에 올렸다.

+0

난이 영역에서 내 자신의 벤치 마크를 실행했고 switch-case가 더 빠른 순서로 발견되었습니다. 내 명령 세트 크기는 현재 약 30입니다. 최근에 gc가 개선되었을 가능성이 있습니까? –

+0

스위치가 어레이 검색보다 빠르다는 것이 이상합니다. 특히 큰 항목의 경우 더욱 그렇습니다. 나는 그것을 이해하지 못한다. :/ – weberc2

+2

충분히 큰 스위치의 경우 더 빠르지 않습니다. 더 작은 스위치는 CPU의 분기 예측기 및 코드 캐시를보다 잘 활용할 수 있기 때문에 더 빨라질 수 있습니다. –

0

만약 당신이 ast의 표현식을 가지고 있고 그것을 많은 양의 데이터 행에 대해 평가하고 싶다면, 람다 트리로 컴파일 한 다음 각 반복마다 스위치를 계산하지 마십시오. 모든; 같은 AST 주어진 예를 들어

: (매우 거친 의사 언어) {* (a, {+ (b, c)})}

컴파일 기능은 다음과 같이 될 것입니다 :

func (e *evaluator) compile(brunch ast) { 
    switch brunch.type { 
    case binaryOperator: 
     switch brunch.op { 
     case *: return func() {compile(brunch.arg0) * compile(brunch.arg1)} 
     case +: return func() {compile(brunch.arg0) + compile(brunch.arg1)} 
     } 
    case BasicLit: return func() {return brunch.arg0} 
    case Ident: return func(){return e.GetIdent(brunch.arg0)} 
    } 
} 

그래서 결국 FUNC 수익을 컴파일, 그가에 호출해야 귀하의 데이터의 각 행과 스위치 또는 기타 계산 물건이 전혀 없을 것입니다. 다른 유형의 데이터를 사용한 작업에 대한 질문이 남아 있습니다. 즉, 자신의 연구를위한 것입니다. 이것은 유용한 점프 테이블 메커니즘이없는 상황에서 흥미로운 방법입니다 :) 그러나 func 호출은 더 복잡합니다. 작동 후 점프.