2010-08-24 6 views
5

C'è un modo per confrontare due blocchi di memoria e sapere in quale punto differiscono (memcmp() non soddisfa questo requisito)? Non vorrei eseguire cicli costosi. Grazie in anticipo.Confronto di memoria (con posizione differenza)

saluti, Neo_b

+0

vedere anche http://stackoverflow.com/questions/855895/intrinsic-memcmp su implementazioni memcmp ottimizzate per-cpu. Se conosci la CPU, puoi sintonizzare una delle funzioni di __builtin_memcmp() di gcc in base alle tue esigenze. – mvds

+1

Nota che tutto ciò che hai qui sarà implementato come un loop * da qualche parte * - non c'è un modo magico di fare ciò che vuoi qui senza uno. –

risposta

2

Rispetto a qualsiasi altra cosa si sta facendo, un ciclo è a buon mercato: il grande costo sarà il recupero dei dati dalla RAM, in primo luogo (o disco!).

2

Non è possibile evitare il ciclo con il confronto della memoria di più di pochi byte. Scrivi l'algoritmo come puoi immaginare. È abbastanza semplice e potresti essere sorpreso dal modo in cui il compilatore ottimizza il codice in questo modo.

4

std::mismatch lo farà per voi in congiunzione std::distance.

+0

Si presume che stia usando l'iteratore STL e inoltre ha bisogno di sapere in quale punto la memoria è diversa. – Doomsday

+0

Avevo std :: equal first che era ovviamente sbagliato quindi l'ho corretto. Gli algoritmi funzionano molto bene con puntatori e con iteratori (full blown). –

+3

@Doomsday: 'char *' * è * un tipo iteratore e 'mismatch' restituisce due iteratori che puntano alla differenza. +1 – Potatoswatter

1

memcmp fa semplicemente un "ciclo costoso", byte per byte. Ad esempio, ecco l'implementazione di Microsoft:

EXTERN_C int __cdecl memcmp(const void *Ptr1, const void *Ptr2, size_t Count) 
{ 
    INT v = 0; 
    BYTE *p1 = (BYTE *)Ptr1; 
    BYTE *p2 = (BYTE *)Ptr2; 

    while(Count-- > 0 && v == 0) { 
     v = *(p1++) - *(p2++); 
    } 

    return v; 
} 

La maggior parte delle altre implementazioni fanno esattamente la stessa cosa. Per le tue esigenze, puoi fare qualcosa del genere:

long my_memcmp(const void *Ptr1, const void *Ptr2, size_t Count) 
{ 
    INT v = 0; 
    long pos = 0; 
    BYTE *p1 = (BYTE *)Ptr1; 
    BYTE *p2 = (BYTE *)Ptr2; 

    while(Count-- > 0 && v == 0) 
    { 
     v = *(p1++) - *(p2++); 
     if (v == 0) 
      pos++; 
     else 
      break; 
    } 

    return pos; 
} 
+0

byte per byte è davvero costoso. Le operazioni 'int' a 32 bit potrebbero essere anche più veloci delle loro controparti a 8 bit. – mvds

+0

Ho creato la mia implementazione (ho pensato di poterlo sostituire con qualcos'altro alla fine). Le mie esigenze richiedono fino a 10.000.000 di iterazioni. Il sistema si blocca a volte, ma funziona. Dice anche quanti byte non corrispondono dopo una prima occorrenza non corrispondente. –

+0

@Neo_b: 10 milioni di iterazioni non sono più così - la maggior parte dei sistemi lo farà in un quarto di secondo o meno. Guarderei il tuo schema di buffer di input, o considerando di ripensare a come stai attaccando questo problema. Se stai cercando le stringhe, ad esempio, l'algoritmo di Boyer Moore probabilmente ti farà meglio di qualsiasi altra cosa qui. –

0

Avrai sempre bisogno di un ciclo. Ma potresti fare un benchmark se il looping di 4 byte (cast in int *) o di 8 byte (uint64_t o long long int) è più veloce della soluzione naive per byte.

Ancora meglio, a seconda della lunghezza (ad esempio,> 1kb) è possibile srotolare il ciclo, il che significa che si controlla ad es. per 8 int/uint64_t e in caso di mancata corrispondenza individuare il primo byte diverso.

uint64_t *bigsteps1 = (uint64_t*)m1; 
uint64_t *bigsteps2 = (uint64_t*)m2; 
int steps = min(m1_len,m2_len)/sizeof(uint64_t); 
int i; 
for (i=0; i<steps; i+=8) 
{ 
    if (bigsteps1[i] != bigsteps2[i] 
     || bigsteps1[i+1] != bigsteps2[i+1] 
    /// .... 
     || bigsteps1[i+7] != bigsteps2[i+7]) break; 
} 

// i<steps tells if we found a difference 
// end game is trivial, left as an excercise to the reader. 

L'unroll ciclo può anche ritorcersi contro, per avere tutte queste cose + N in là e i + = 8 così. Punto di riferimento per essere sicuro.

ps controllare anche l'allineamento della memoria: questo sarà il più veloce quando m1&0xff == m2&0xff == 0

+0

Grazie per il consiglio, sicuramente lo implementerò, anche se non sono del tutto sicuro che cosa dovrebbe fare m1 & 0xff == m2 & 0xff == 0, da quello che so m1 & 0xff == m1, non è corretto? –

+0

In alcuni casi sarà più veloce, ma potrebbe causare alcuni problemi. Prima di tutto, si basa sulla tua piattaforma che ha lo stesso allineamento per interi a 64 bit come per i caratteri, che spesso non è il caso. (Nessuno dice che la base dell'array di caratteri debba essere su un limite di 8 byte) Secondo, un intrinseco intrinseco o assemblatore sarà probabilmente più veloce. Su x86, il problema dell'allineamento della memoria renderà le cose più lente e su altre architetture causerà un interrupt al processore. –

+0

@Neo_b: 'm1 & 0xff == 0' è un test se l'indirizzo' m1' termina con '00'. @Billy: Ovviamente in questo tipo di ottimizzazioni devi giocare un po 'con i limiti, quindi fino a quando il primo blocco allineato si verifica lentamente, quindi testare il maggior numero possibile di blocchi velocemente e testare il resto lentamente. (come affermato queste cose funzionano solo in modo positivo se i blocchi sono abbastanza grandi) Un intrinseco intrinseco o assemblatore sarà probabilmente più veloce * se esistesse * che, a mio avviso, non è il caso del problema in questione. – mvds

1

Se ci fosse un modo migliore di confronto tra due blocchi di memoria, memcmp sarebbe reimplementato per farlo.

Detto questo, memcmp ha un'implementazione portatile predefinita nella libreria C standard, ma spesso viene implementata dal compilatore stesso come funzione incorporata. Questa funzione incorporata dovrebbe essere altamente ottimizzata per l'architettura di destinazione. Quindi prendi l'implementazione della libreria con un pizzico di sale.