- Compiler: PP → Lexer → Parser → Analyzer → Gengo pipeline - Parser: 232/236 (98%) Harbour compatibility, registry-based dispatch - RTL: 351 Harbour-compatible functions - RDD: DBF/NTX/CDX engines with Rushmore bitmap optimization - Go Interop: IMPORT + pkg.Func() + obj:Method() with FastPath (15M calls/sec) - HB_FUNC API: Full Harbour C API compatible Go bridge - Concurrency: SPAWN/LAUNCH/GOROUTINE, <-, WATCH, PARALLEL FOR, ASYNC/AWAIT - Extensions: Multi-return, DEFER, Slice, f-string, Nil-safe ?:, CONST - Macro Compiler: Runtime AST parsing and evaluation - Debugger: TUI debugger with source display, breakpoints, stepping - FRB: Native + Pcode dual mode runtime binary - Tests: 13 packages ALL PASS Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
6.3 KiB
Five Go Interop Performance
핵심 요약
PRG에서 Go 함수를 호출할 때, Five는 자동으로 3단계 최적화를 적용합니다. 개발자가 코드를 바꿀 필요 없음 — gengo가 알아서 최적 경로를 선택.
벤치마크 결과 (Intel Ultra 7 255H)
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
Function Direct Go Reflect FastPath 개선
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
strings.ToUpper 34ns 243ns 66ns 3.7x
strings.Contains 3ns 218ns 19ns 11.7x
strings.ReplaceAll 43ns 339ns 77ns 4.4x
math.Sqrt 0.1ns 175ns 16ns 11.0x
obj:Method() — 416ns 233ns 1.8x
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
메모리 할당 1회 7~9회 1~3회 3x 절약
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
3단계 자동 최적화
1단계: FastPath — 패키지 함수 호출 (3~12x 빠름)
cResult := strings.ToUpper(cText)
gengo가 자동 생성:
// 컴파일 시점에 타입 특화 함수 등록
var _ff_strings_ToUpper = hbrt.RegisterFastFunc("strings.ToUpper", strings.ToUpper)
// 런타임: reflect 우회, 타입 assertion 직접 호출
_results := hbrt.GoCallFast(_ff_strings_ToUpper, _a0) // 66ns
RegisterFastFunc가 함수 시그니처를 감지하여 자동으로 fast path 설정:
func(string) string→ 직접 호출func(string, string) bool→ 직접 호출func(float64) float64→ 직접 호출- 그 외 → reflect fallback
2단계: Method Cache — 객체 메서드 호출 (1.8x 빠름)
db:Exec("CREATE TABLE ...")
gengo가 자동 생성:
// 첫 호출: reflect.MethodByName → 캐시 저장
// 이후 호출: 캐시에서 즉시 조회
hbrt.GoCallCached(_obj, "Exec", _sa0) // 233ns (vs 416ns)
동일 타입의 동일 메서드를 반복 호출할 때 lookup 비용 제거.
3단계: 자동 타입 변환 — 메모리 3x 절약
| PRG 타입 | Go 타입 | 변환 비용 |
|---|---|---|
| String | string | zero-copy (포인터 전달) |
| Numeric(int) | int | 비트 캐스트 (0 alloc) |
| Numeric(double) | float64 | 비트 캐스트 (0 alloc) |
| Logical | bool | 비트 캐스트 (0 alloc) |
| Array | []T | 슬라이스 변환 (1 alloc) |
실전 성능 비교
10만 건 문자열 변환
FOR i := 1 TO 100000
aData[i] := strings.ToUpper(aData[i])
NEXT
| 방식 | 10만 건 | 100만 건 |
|---|---|---|
| Reflect (구버전) | 24ms | 243ms |
| FastPath (현재) | 6.6ms | 66ms |
| Native Go | 3.4ms | 34ms |
PRG 코드가 native Go의 2배 이내 성능.
DB 대량 조회
aRows := SqlQuery(db, "SELECT * FROM products") // 10만 건
FOR i := 1 TO Len(aRows)
aRows[i]["name"] := strings.ToUpper(aRows[i]["name"])
NEXT
| 단계 | 시간 |
|---|---|
| SQL 쿼리 (Go database/sql) | ~50ms |
| 결과 변환 (Go → Harbour) | ~15ms |
| 문자열 처리 (FastPath) | ~7ms |
| 총 합계 | ~72ms |
순수 Go 프로그램: ~55ms. 오버헤드 30% 미만.
HTTP 서버 요청 처리
// 요청마다 strings.Contains, fmt.Sprintf 등 호출
| 항목 | 처리량 |
|---|---|
| Go net/http 자체 | ~100,000 req/sec |
| Five PRG 핸들러 (FastPath) | ~80,000 req/sec |
| Five PRG 핸들러 (Reflect) | ~30,000 req/sec |
FastPath로 HTTP 서버도 Go native의 80% 성능.
언제 차이가 나는가
차이 없음 (단일 호출)
db := sql.Open("sqlite", ":memory:") // 1회 호출 — 66ns vs 243ns = 무의미
cResult := strings.ToUpper("hello") // 1회 호출 — 체감 불가
차이 큼 (대량 반복)
FOR i := 1 TO 100000 // 10만 회 반복
aData[i] := strings.ToUpper(aData[i]) // FastPath: 6.6ms vs Reflect: 24ms
NEXT
DO WHILE rows:Next() // DB 전체 스캔
? rows:Column(1) // Cached: 23ms vs Reflect: 42ms
ENDDO
Five vs 다른 언어 인터롭 비교
| 언어 | 외부 호출 방식 | 오버헤드 |
|---|---|---|
| Python → C (ctypes) | FFI marshal | ~1,000ns |
| Java → C (JNI) | JNI bridge | ~100ns |
| Node.js → C (N-API) | V8 bridge | ~200ns |
| Five → Go (FastPath) | 타입 assertion | 16~77ns |
| Five → Go (Method) | reflect + cache | 233ns |
Five의 Go interop은 JNI보다 빠르고, Python ctypes보다 10배 빠릅니다.
스트레스 테스트 결과
Volume: 40,000 calls (string/int/float/bool × 10,000) ✅
Large Data: 1MB string, 10,000 array, 1,000 map ✅
Boundary: int/int64/float64/string 극한값 ✅
Concurrent: 20,000 goroutine 동시 호출 ✅
Object: 1,000 객체 생성, method chain, nil safety ✅
Coercion: 7 × 6 = 42 타입 조합 중 41 성공 ✅
Fuzz: 5,000 랜덤 입력 검증 ✅
왜 빠른가 — 기술적 배경
-
컴파일 타임 결정: gengo가 IMPORT된 패키지를 분석하여 FastFunc 등록 코드 생성. 런타임 판단 비용 제로.
-
타입 특화:
func(string) string같은 common 시그니처는reflect.Call대신 Go 타입 assertion으로 직접 호출. alloc 7회 → 1회. -
메서드 캐시: 동일 타입+메서드명의
reflect.Methodlookup을sync.RWMutex보호 map에 캐시. 두 번째 호출부터 lookup 비용 제거. -
Zero-copy 문자열: Harbour의
HbString과 Go의string은 모두 불변(immutable). 포인터만 전달하면 복사 불필요. -
24바이트 Value: Five의 Tagged Value는 24바이트 고정 크기. 스택 할당 가능, GC 압박 최소.