2015-02-02 21 views
10

Sto facendo un test: confronta i tempi di esecuzione di cgo e le funzioni di puro Go eseguono 100 milioni di volte ciascuna. La funzione cgo impiega più tempo rispetto alla funzione Golang e sono confuso da questo risultato. Il mio codice di prova è:Perché le prestazioni di cgo sono così lente? c'è qualcosa di sbagliato nel mio codice di test?

package main 

import (
    "fmt" 
    "time" 
) 

/* 
#include <stdio.h> 
#include <stdlib.h> 
#include <string.h> 

void show() { 

} 

*/ 
// #cgo LDFLAGS: -lstdc++ 
import "C" 

//import "fmt" 

func show() { 

} 

func main() { 
    now := time.Now() 
    for i := 0; i < 100000000; i = i + 1 { 
     C.show() 
    } 
    end_time := time.Now() 

    var dur_time time.Duration = end_time.Sub(now) 
    var elapsed_min float64 = dur_time.Minutes() 
    var elapsed_sec float64 = dur_time.Seconds() 
    var elapsed_nano int64 = dur_time.Nanoseconds() 
    fmt.Printf("cgo show function elasped %f minutes or \nelapsed %f seconds or \nelapsed %d nanoseconds\n", 
     elapsed_min, elapsed_sec, elapsed_nano) 

    now = time.Now() 
    for i := 0; i < 100000000; i = i + 1 { 
     show() 
    } 
    end_time = time.Now() 

    dur_time = end_time.Sub(now) 
    elapsed_min = dur_time.Minutes() 
    elapsed_sec = dur_time.Seconds() 
    elapsed_nano = dur_time.Nanoseconds() 
    fmt.Printf("go show function elasped %f minutes or \nelapsed %f seconds or \nelapsed %d nanoseconds\n", 
     elapsed_min, elapsed_sec, elapsed_nano) 

    var input string 
    fmt.Scanln(&input) 
} 

e il risultato è il seguente:

cgo show function elasped 0.368096 minutes or 
elapsed 22.085756 seconds or 
elapsed 22085755775 nanoseconds 

go show function elasped 0.000654 minutes or 
elapsed 0.039257 seconds or 
elapsed 39257120 nanoseconds 

I risultati mostrano che invocando la funzione C è più lenta rispetto alla funzione Go. C'è qualcosa di sbagliato nel mio codice di test?

Il mio sistema è: Mac OS X 10.9.4 (13E28)

+0

Perché pensi che chiamare una funzione C da Go dovrebbe essere più veloce di chiamare una funzione Go da Go? – Volker

+0

Mi aspetto che il go code incorpori la versione go di 'show()' che è un ulteriore vantaggio del codice go su 'C.show()'. –

risposta

25

Come hai scoperto, il costo del codice C/C++ via CGo è piuttosto elevato. Quindi, in generale, è meglio cercare di ridurre al minimo il numero di chiamate CGo effettuate. Per l'esempio precedente, piuttosto che chiamare ripetutamente una funzione CGo in un ciclo, potrebbe avere senso spostare il ciclo in basso su C.

Ci sono un certo numero di aspetti di come il runtime Go imposta i suoi thread che possono rompere il aspettative di molti pezzi di codice C:

  1. Goroutines eseguito su una parte relativamente piccola pila, la gestione della crescita pila attraverso le pile segmentati (vecchie versioni) o copiando (nuove versioni).
  2. I thread creati dal runtime Go potrebbero non interagire correttamente con l'implementazione di archiviazione locale del thread di libpthread.
  3. Il gestore di segnale UNIX di runtime di Go può interferire con il codice C o C++ tradizionale.
  4. Go riutilizza i thread del sistema operativo per eseguire più goroutine. Se il codice C chiama una chiamata di sistema di blocco o monopolizza in altro modo il thread, potrebbe essere dannoso per altre goroutine.

Per questi motivi, CGo sceglie l'approccio sicuro per l'esecuzione del codice C in un thread separato impostato con uno stack tradizionale.

Se provieni da lingue come Python, dove non è raro riscrivere gli hotspot di codice in C come un modo per accelerare un programma, resterai deluso. Ma allo stesso tempo, c'è un divario molto più piccolo nelle prestazioni tra codice C e Go equivalente.

In generale, riservo CGo per l'interfaccia con le librerie esistenti, possibilmente con piccole funzioni wrapper C che possono ridurre il numero di chiamate che devo effettuare da Go.

+0

Grazie, mi aiuta molto! –

+0

Probabilmente è obsoleto: https://groups.google.com/forum/#!topic/golang-nuts/RTtMsgZi88Q – gavv

-1

C'è un po 'in testa nel chiamare funzioni C da Go. Questo non può essere cambiato.

8

Aggiornamento per James answer: sembra che non ci sia alcun cambio di thread nell'attuale implementazione.

Vedi this thread su golang-dadi:

C'è sempre sarà certo overhead. È più costoso di una semplice chiamata di funzione ma molto meno costoso di uno switch di contesto (si ricorda una precedente implementazione abbiamo interrotto l'interruttore di thread prima della versione pubblica). In questo momento la spesa è fondamentalmente solo dover fare uno switch set di registro completo (nessun coinvolgimento del kernel). Suppongo che sia paragonabile a dieci chiamate di funzione.

Vedere anche this answer che collega il post del blog "cgo is not Go".

C non sa nulla di convenzione di chiamata del Go o pile growable, quindi una chiamata verso il basso per il codice C deve registrare tutti i dettagli dello stack goroutine, passare alla pila C, ed eseguire codice C che non ha conoscenza di come è stato invocato, o del più grande runtime di Go in funzione del programma.

Così, CGO ha un sovraccarico in quanto svolge un interruttore pila, non infilare interruttore.

Salva e ripristina tutti i registri quando viene chiamata la funzione C, mentre non è richiesta quando viene chiamata la funzione Go o la funzione assembly.


Oltre a ciò, le convenzioni di chiamata del CGO vietano passando puntatori andare direttamente al codice C, e soluzione comune è quello di utilizzare C.malloc, e così di introdurre ulteriori accantonamenti. Vedi this question per i dettagli.

Problemi correlati