2013-05-22 11 views
7

Sto lavorando su un progetto C++ molto grande, ha molte funzioni critiche in tempo reale e anche molte funzioni di background lento. Queste funzioni in background non devono essere chiamate da funzioni critiche per il tempo. Quindi c'è modo di rilevare queste funzioni in background chiamate da funzioni critiche? il tempo di compilazione sarebbe buono ma comunque mi piace rilevare prima queste funzioni di sfondo. Ulteriori informazioni, entrambe le funzioni lente e critiche fanno parte della stessa classe e condividono la stessa intestazione.Come rilevare la funzione di sfondo chiamata in funzioni critiche

Altre informazioni, le funzioni critiche vengono eseguite con un thread molto più veloce (> = 10 KHz) più lento viene eseguito con thread più lento diverso (< = 1KHz). Le variabili dei membri della classe sono protette mediante sezioni critiche in funzioni lente poiché entrambe utilizzano le stesse variabili membro della classe. Questa è la ragione per cui le funzioni lente nelle funzioni critiche rallenteranno le prestazioni generali del sistema. Questa è la ragione per cui mi piace trovare tutti questi tipi di funzioni automaticamente anziché il controllo manuale.

Grazie ....

+0

È questo codice multi-threaded? –

+0

Sì @AdrianMcCarthy – user2409054

risposta

3

Ottenere un rilevamento in fase di compilazione diverso da quello proposto da Nicholas Wilson sarà estremamente difficile se non impossibile, ma presupponendo che lo "sfondo" si riferisca realmente alle funzioni e non a più thread (non ho visto alcun riferimento ai thread in la domanda, quindi presumo che sia solo una formulazione strana) potresti banalmente usare una bandiera globale e un oggetto armadietto, e assert o lanciare un'eccezione. Oppure, emettere un messaggio di debug. Questo, ovviamente, sarà solo runtime - ma dovresti essere in grado di isolare molto velocemente i criminali. Sarà anche un sovraccarico molto basso per le build di debug (quasi garantito per l'esecuzione dalla cache L1), e nessuna per le build di rilascio.

Utilizzando CaptureStackBackTrace, si dovrebbe essere in grado di acquisire l'indirizzo della funzione incriminata, che uno strumento come addr2line (o qualunque sia l'equivalente MS) può tradurre direttamente in una riga del codice. Probabilmente esiste anche una funzione di toolhelp che può eseguire direttamente questa traduzione (anche se non lo saprei).

Quindi, qualcosa di simile (non testato!) Potrebbe fare il trucco:

namespace global { int slow_flag = 0; } 
struct slow_func_locker 
{ 
    slow_func_locker() { ++global::slow_flag; } 
    ~slow_func_locker(){ --global::slow_flag; } 
}; 
#indef NDEBUG 
    #define REALTIME if(global::slow_flag) \ 
    { \ 
    void* backtrace; \ 
    CaptureStackBackTrace(0, 1, &backtrace, 0); \ 
    printf("RT function %s called from %08x\n", __FUNCTION__, backtrace); \ 
    } 
    #define SLOW_FUNC slow_func_locker slow_func_locker_; 
#else 
    #define REALTIME 
    #define SLOW_FUNC 
#endif 

foo_class::some_realtime_function(...) 
{ 
    REALTIME; 
    //... 
}; 

foo_class::some_slow_function(...) 
{ 
    SLOW_FUNC; 
    //... 
    some_realtime_function(blah); // this will trigger 
}; 

L'unico vero problema (oltre a non essere a tempo di compilazione) è che devi marcare ogni funzione lento e in tempo reale con entrambi i marcatori, ma dal momento che il compilatore non può sapere magicamente quale è ciò, non c'è comunque molta scelta.

Si noti che la "bandiera" globale è in realtà un contatore , non una bandiera. La ragione di ciò è che una funzione lenta potrebbe immediatamente chiamare un'altra funzione lenta che ritorna e cancella il flag - assumendo erroneamente una funzione veloce ora (l'approccio con le sezioni critiche suggerito da xgbi potrebbe in questo caso deadlock!). Un contatore impedisce che ciò accada. In presenza di fili, è possibile sostituire int con std::atomic_int.

EDIT:
Come è chiaro ora che ci sono davvero 2 discussioni in esecuzione, e importa solo che uno di loro (il filo "veloce") non significa mai chiamare una funzione "lento", c'è un'altra soluzione semplice e funzionante (ad esempio utilizzando l'API Win32, ma potrebbe essere eseguita con POSIX in entrambi i casi):

All'avvio del thread "veloce" (il thread "lento" non ha bisogno di farlo), store l'ID del thread da qualche parte, sia come variabile globale, sia come membro dell'oggetto che contiene tutte le funzioni veloce/lento, ovunque sia accessibile:

global::fast_thread_id = GetCurrentThreadId(); 

La macro per salvare su chiamate di funzione "sgraditi" potrebbe quindi apparire come:

#define CHECK_FAST_THREAD assert(GetCurrentThreadID() != global::fast_thread_id) 

Questa macro viene poi aggiunto a qualsiasi funzione di "slow" che non dovrebbe mai essere chiamato dal thread "veloce" . Se il thread veloce chiama una funzione che non deve chiamare, il trigger asserisce ed è noto quale funzione viene chiamata.

+0

Questo è molto simile a un'idea che stavo per suggerire, ma stavo per avere il contatore dirmi se sono in un percorso critico. Sicuramente va bene chiamare una funzione critica da una funzione lenta, ma non viceversa. Destra? –

+0

@AdrianMcCarthy: anch'io ritengo che dal momento che le funzioni "critiche" sono veloci, non ci dovrebbe essere nulla contro il chiamarlo da una funzione "lenta". Quindi solo l'assegno. Ma naturalmente si potrebbe aggiungere banalmente il contrario. – Damon

+1

Il mio punto è che il controllo che hai è meno utile. Dovresti cambiare la logica. –

4

È necessario sfruttare il linker. Separare le funzioni "in tempo reale" e quelle lente in due moduli e collegarli nell'ordine corretto.

Ad esempio, suddividere i file in due directory. Creare un lib da ogni directory (ranlib file insieme l'oggetto) e poi collegare l'applicazione finale utilizzando:

c++ -o myapp main.o lib1/slowfns.a lib2/realtime.a 

Se si tenta di chiamare qualsiasi cosa, da slowfns.a in realtime.a, a seconda del compilatore, fallirà per collegare (alcuni compilatori potrebbero aver bisogno di opzioni per far rispettare questo).

Inoltre, questo consente di gestire facilmente anche dichiarazioni in fase di compilazione: assicurarsi che le intestazioni dalla libreria slowfns non si trovino sul percorso di inclusione quando si compila la libreria di funzioni "realtime" per una maggiore protezione.

+0

Questo non risponde alla domanda di rilevamento quando le funzioni in background sono in esecuzione. – metalhead

+0

Grazie Nicholas. Buon suggerimentoAttualmente ho una dichiarazione di classe nel file di intestazione singola e la stessa classe avrà funzioni lente e critiche e dichiarata nello stesso file di intestazione. Implementazioni di funzioni lente e critiche sono in file diversi ma condividono lo stesso file di intestazione poiché entrambi fanno parte della stessa classe. Inoltre non possiamo dividere l'intestazione della classe. Per questo motivo potrei non essere in grado di fare ciò che hai suggerito. Hai altre idee sul mio scenario attuale? Grazie. – user2409054

+0

Otterrai comunque la protezione del tempo di collegamento con la configurazione. ranlib i due file oggetto che stai producendo in due diversi file .a. (Sto controllando, quale compilatore è questo? Sto assumendo gcc/icc/xlc o simili.) –

3

Non so come farlo al momento della compilazione, ma per il runtime, forse usare un mutex?

static Mutex critical_mutex; 
#define CALL_SLOW(f) if(critical_mutex.try_lock() == FAIL) \ 
    printf("SLOW FUNCTION " #f" called while in CRITICAL\n");\ 
    f 

#define ENTER_CRITICAL() critical_mutex.lock() 
#define EXIT_CRITICAL() critical_mutex.unlock() 

Ogni volta che si utilizza una funzione lenta mentre si è in una sezione critica, il trilock fallirà.

void slow_func(){ 
} 

ENTER_CRITICAL(); 
CALL_SLOW(slow_func()); 
EXIT_CRITICAL(); 

stamperà:

SLOW FUNCTION slow_func() called while in CRITICAL 

Se avete bisogno di velocità, è possibile implementare il mutex leggero con interlockedincrement su Windows o su Linux __sync* funzioni.
Preshing ha una serie impressionante di post di blog su questo HERE.

+0

È una buona idea, ma dovrò controllare questo controllo mutex prima di chiamare ogni funzione di sfondo. Ho migliaia di funzioni in background e potrebbe aggiungere un sovraccarico alle prestazioni con mutex e controllo extra. Aspetterò altre idee. Grazie per la tua risposta. – user2409054

+0

È possibile utilizzare [interlockedincrement] (http://msdn.microsoft.com/en-us/library/windows/desktop/ms683614 (v = vs.85) .aspx) in Windows o [__sync *] (http://gcc.gnu.org/onlinedocs/gcc-4.3.2/gcc/Atomic-Builtins.html) funzioni su linux per avere un mutex molto ligtweight. – Gui13

1

Se si è liberi di modificare il codice come si desidera, esiste una soluzione a livello di sistema di tipo che comporta l'aggiunta di alcune piastre.

Fondamentalmente, si crea una nuova classe, SlowFunctionToken. Ogni funzione lenta nel tuo programma prende un riferimento a SlowFunctionToken. Successivamente, si rendono privati ​​i costruttori di default di SlowFunctionToken e si copia.

Ora solo le funzioni che hanno già un SlowFunctionToken possono chiamare funzioni lente. Come si ottiene un SlowFunctionToken? Aggiungi dichiarazioni di amici a SlowFunctionToken; in particolare, si consiglia alle funzioni di immissione dei thread dei thread che sono autorizzati a utilizzare le funzioni lente. Quindi, creare oggetti SlowFunctionToken locali lì e passarli verso il basso.

class SlowFunctionToken; 

class Stuff { 
public: 
    void FastThread(); 
    void SlowThread(); 

    void ASlowFunction(SlowFunctionToken& sft); 
    void AnotherSlowFunction(SlowFunctionToken& sft); 

    void AFastFunction(); 
}; 

class SlowFunctionToken { 
    SlowFunctionToken() {} 
    SlowFunctionToken(const SlowFunctionToken&) {} 

    friend void Stuff::SlowThread(); 
}; 

void Stuff::FastThread() { 
    AFastFunction(); 
    //SlowFunctionToken sft; doesn't compile 
    //ASlowFunction(???); doesn't compile 
} 

void Stuff::SlowThread() { 
    SlowFunctionToken sft; 
    ASlowFunction(sft); 
} 

void Stuff::ASlowFunction(SlowFunctionToken& sft) { 
    AnotherSlowFunction(sft); 
    AFastFunction(); // works, but that function can't call slow functions 
} 
+0

Sembra buono, ma potrei non essere in grado di implementare questa logica a causa della fase corrente del mio progetto. – user2409054

Problemi correlati