The example illustrates a "clock-time" benchmark for assessing detection speed.Using a YAML formatted evidence file - "20000 Evidence Records.yml" - supplied with the distribution or can be obtained from the data repository on Github.
It's important to understand the trade-offs between performance, memory usage and accuracy, that the 51Degrees pipeline configuration makes available, and this example shows a range of different configurations to illustrate the difference in performance.
Requesting properties from a single component reduces detection time compared with requesting properties from multiple components. If you don't specify any properties to detect, then all properties are detected.
#include <stdio.h>
#include <time.h>
 
 
#include "ExampleBase.h"
#define DEFAULT_NUMBER_OF_THREADS 2
#define DEFAULT_ITERATIONS_PER_THREAD 10000
#define SIZE_OF_KEY 500
#define SIZE_OF_VALUE 1000
#define MAX_EVIDENCE 20
static const char* dataDir = "device-detection-data";
static const char* dataFileName = "51Degrees-LiteV4.1.hash";
static const char* evidenceFileName = "20000 Evidence Records.yml";
static const char* userAgent = "user-agent";
typedef struct performanceConfig_t {
    
    
    bool allProperties;
typedef struct benchmarkResult_t {
    
    long count;
    
    double elapsedMillis;
    
    
    unsigned long checkSum;
typedef struct evidence_node_t {
typedef struct shared_string_node_t {
    const char* value;
    size_t length;
typedef struct performanceState_t {
    
    
    uint16_t numberOfThreads;
    
    
    
    
    
    int evidenceCount;
    
    int iterationsPerThread;
    
    const char* dataFileLocation;
    
    FILE* output;
    
    FILE* resultsOutput;
    
    
    
    double startUpMillis;
    
    int availableProperties;
    
    int maxEvidence;
typedef struct threadState_t {
    
    
static const char* getOrAddSharedString(
    const char* target) {
    while (node != NULL) {
        if (strcmp(target, node->
value) == 0) {
             return node->value;
        }
    }
    
    
    size_t length = strlen(target) + 1;
    if (node == NULL) {
        return NULL;
    }
    node->next = NULL;
    node->value = (
const char*)
Malloc(
sizeof(
char) * length);
    if (node->value == NULL) {
        return NULL;
    }
    if (strncpy((char*)node->value, target, length) == NULL) {
        return NULL;
    }
    
    
    if (perfState->sharedStringFirst == NULL) {
        perfState->sharedStringFirst = node;
        perfState->
sharedStringLast = node;
    }
    else {
        perfState->sharedStringLast->next = node;
        perfState->sharedStringLast = node;
    }
    
    return node->value;
}
static void storeEvidence(
KeyValuePair* pairs, uint16_t size, 
void* state) {
     char* ptr;
    
    
    if (perfState->
evidenceFirst == NULL) {
         perfState->evidenceFirst = node;
        perfState->
evidenceLast = node;
    }
    else {
        perfState->evidenceLast->next = node;
        perfState->evidenceLast = node;
    }   
    
    for (uint32_t i = 0; i < size; i++) {
        
        if (prefix != NULL) {
            
            
            
            evidence->items[i].field = getOrAddSharedString(
                 perfState, 
            }
        }
        else {
        }
        
        
            strcmp(
evidence->items[i].field, userAgent) == 0) {
            
            
            
                sizeof(
char) * (pairs[i].
valueLength + 1));
             ptr = strncpy(
                (
char*)
evidence->items[i].originalValue,
                pairs[i].valueLength);
            if (ptr == NULL) {
            }
        }
        else {
            
            
            
            evidence->items[i].parsedValue = getOrAddSharedString(
                 perfState,
                pairs[i].value);
            if (
evidence->items[i].parsedValue == NULL) {
             }
            evidence->items[i].originalValue = NULL;
         }
    }
    
    if (size > perfState->
maxEvidence) {
         perfState->maxEvidence = size;
    }
    
    perfState->
evidenceCount++;
}
void runPerformanceThread(void* state) {
    
        &thisState->
mainState->
manager,
        thisState->mainState->maxEvidence,
        thisState->mainState->maxEvidence);
    
    
        thisState->mainState->maxEvidence);
    TIMER_CREATE;
    TIMER_START;
    
    while(node != NULL) {
        
        
        
        
        
        
                results,
                j,
                if (value != NULL) {
                    
                    
                    thisState->
result->
checkSum += value->
size;
                }
            }
        }
        
        thisState->result->
count++;
        
        node = node->next;
    }
    
    TIMER_END;
    
    thisState->result->
elapsedMillis = TIMER_ELAPSED;
    }
}
    
    for (int i = 0; i < state->numberOfThreads; i++) {
        states[i].mainState = state;
        states[i].result = &state->
resultList[i];
        states[i].result->checkSum = 0;
        states[i].result->count = 0;
        states[i].result->elapsedMillis = 0;
    }
    int thread;
    TIMER_CREATE;
    TIMER_START;
        
        for (thread = 0; thread < state->numberOfThreads; thread++) {
                &states[thread]);
        }
        
        for (thread = 0; thread < state->numberOfThreads; thread++) {
        }
    }
    else {
        fprintf(state->
output, 
"Example not build with multi threading support.\n");
        runPerformanceThread(&states[0]);
    }
    TIMER_END;
    return TIMER_ELAPSED;
}
    double totalMillis = 0;
    long totalChecks = 0;
    long checksum = 0;
    for (int i = 0; i < state->numberOfThreads; i++) {
        fprintf(state->output,
            "Thread: %ld detections, elapsed %.3f seconds, %.0lf Detections per second\n",
            result->count,
            result->elapsedMillis / 1000.0,
            round(1000.0 * result->count / result->elapsedMillis));
        totalMillis += result->elapsedMillis;
        totalChecks += result->count;
        checksum += result->checkSum;
    }
    
    double millisPerTest = ((double)totalMillis / (state->numberOfThreads * totalChecks));
    fprintf(state->output,
        "Overall: %ld detections, Average ms per detection: %f, Detections per second: %.0lf\n",
        totalChecks,
        millisPerTest,
        round(1000.0 / millisPerTest));
    fprintf(state->output,
        "Overall: Concurrent threads: %d, Checksum: %lx\n",
        state->numberOfThreads,
        checksum);
    fprintf(state->output,
        "Overall: Startup ms %.0lf\n",
    fprintf(state->output,
        "Overall: Properties retrieved %d\n",
        state->
availableProperties);
    fprintf(state->output, "\n");
    if (state->
resultsOutput != NULL) {
         fprintf(state->resultsOutput, "  \"DetectionsPerSecond\": %.2f,\n", round(1000.0 / millisPerTest));
        fprintf(state->resultsOutput, "  \"StartupMs\": %.0lf,\n", state->startUpMillis);
    }
}
void executeBenchmark(
    
    fprintf(state->output, 
        "Benchmarking with profile: %s AllProperties: %s\n",
        fiftyoneDegreesExampleGetConfigName(dataSetConfig),
        config.
allProperties ? 
"True" : 
"False");
    
    if (config.allProperties == false) {
        properties.
string = 
"IsMobile";
    }
    
    fprintf(state->output, "Load from disk\n");
    
    TIMER_CREATE;
    TIMER_START;
    
        &state->manager,
        &dataSetConfig,
        &properties,
        exception);
        fprintf(state->output, "%s\n", message);
        return;
    }
    
    TIMER_END;
    state->startUpMillis = TIMER_ELAPSED;
    
    fiftyoneDegreesExampleCheckDataFile(dataset);
    
    fprintf(state->output, "Warming up\n");
    runTests(state);
    fprintf(state->output, "Running\n");
    double executionTime = runTests(state);
    fprintf(state->output,
        "Finished - Execution time was %lf ms\n",
        executionTime);
    doReport(state);
}
    while (node != NULL) {
        for (uint32_t j = 0; j < 
evidence->count; j++) {
             if (
evidence->items[j].originalValue != NULL) {
             }
        }
        node = next;
    }
}
    while (node != NULL) {
        Free((
void*)node->value);
         node = next;
    }
}
void fiftyoneDegreesHashPerformance(
    const char* dataFilePath,
    const char* evidenceFilePath,
    uint16_t numberOfThreads,
    int iterationsPerThread,
    FILE* output,
    FILE* resultsOutput) {
    
    fprintf(output, "Running Performance example - ");
        fprintf(output, "optimised build\n");
    }
    else {
        fprintf(output, "standard build\n");
        printf("\033[0;33m");
        fprintf(
            output,
            "Use FIFTYONE_DEGREES_MEMORY_ONLY directive for optimum " \
            "performance\n");
        printf("\033[0m");
    }
    state.dataFileLocation = dataFilePath;
    state.output = output;
    state.resultsOutput = resultsOutput;
    state.evidenceCount = 0;
        state.numberOfThreads = numberOfThreads;
    }
    else {
        state.numberOfThreads = 1;
    }
    state.
iterationsPerThread = iterationsPerThread;
    
    char buffer[MAX_EVIDENCE * (SIZE_OF_KEY + SIZE_OF_VALUE)];
    char key[MAX_EVIDENCE][SIZE_OF_KEY];
    char value[MAX_EVIDENCE][SIZE_OF_VALUE];
    for (int i = 0; i < MAX_EVIDENCE; i++) {
        pair[i].key = key[i];
        pair[i].keyLength = SIZE_OF_KEY;
        pair[i].value = value[i];
        pair[i].valueLength = SIZE_OF_VALUE;
    }
    
    
    fprintf(
        state.output,
        "Reading '%i' evidence records into memory.\n",
        state.iterationsPerThread);
    
    
    state.evidenceCount = 0;
    state.maxEvidence = 0;
    state.evidenceFirst = NULL;
    state.evidenceLast = NULL;
    state.sharedStringFirst = NULL;
    state.sharedStringLast = NULL;
        evidenceFilePath,
        buffer,
        sizeof(buffer),
        pair,
        MAX_EVIDENCE,
        state.iterationsPerThread,
        &state,
        storeEvidence);
    
    fprintf(
        state.output,
        "Read '%i' evidence records into memory.\n",
        state.evidenceCount);
    if (state.resultsOutput != NULL) {
        fprintf(state.resultsOutput, "{");
    }
    
    for (int i = 0;
        i++) {
        
            
            if (state.resultsOutput != NULL) {
                fprintf(state.resultsOutput, "%s\n\"%s%s\": {\n",
                    i > 0 ? "," : "",
                    fiftyoneDegreesExampleGetConfigName(*(performanceConfigs[i].config)),
                    performanceConfigs[i].allProperties ? "_All" : "");
            }
            executeBenchmark(&state, performanceConfigs[i]);
            if (state.resultsOutput != NULL) {
                fprintf(state.resultsOutput, "}");
            }
        }
    }
    
    if (state.resultsOutput != NULL) {
        fprintf(state.resultsOutput, "}\n");
    }
    
    freeSharedStrings(&state);
    
    freeEvidence(&state);
    
    fprintf(output, "Finished Performance example\n");
}
void fiftyoneDegreesExampleCPerformanceRun(ExampleParameters* params) {
    
    fiftyoneDegreesHashPerformance(
        params->dataFilePath,
        params->evidenceFilePath,
        params->numberOfThreads,
        params->iterationsPerThread,
        params->output,
        params->resultsOutput);
}
#ifndef TEST
#define DATA_OPTION "--data-file"
#define DATA_OPTION_SHORT "-d"
#define UA_OPTION "--user-agent-file"
#define UA_OPTION_SHORT "-u"
#define THREAD_OPTION "--threads"
#define THREAD_OPTION_SHORT "-t"
#define JSON_OPTION "--json-output"
#define JSON_OPTION_SHORT "-j"
#define ITERATIONS_OPTION "--iterations"
#define ITERATIONS_OPTION_SHORT "-i"
#define HELP_OPTION "--help"
#define HELP_OPTION_SHORT "-h"
#define OPTION_PADDING(o) ((int)(30 - strlen(o)))
#define OPTION_MESSAGE(m, o, s) printf("  %s, %s%*s: %s\n", o, s, OPTION_PADDING(o), " ", m);
void printHelp() {
    printf("Available options are:\n");
    OPTION_MESSAGE("Path to a 51Degrees Hash data file", DATA_OPTION, DATA_OPTION_SHORT);
    OPTION_MESSAGE("Path to a User-Agents YAML file", UA_OPTION, UA_OPTION_SHORT);
    OPTION_MESSAGE("Number of threads to run", THREAD_OPTION, THREAD_OPTION_SHORT);
    OPTION_MESSAGE("Number of iterations per thread", ITERATIONS_OPTION, ITERATIONS_OPTION_SHORT);
    OPTION_MESSAGE("Path to a file to output JSON format results to", JSON_OPTION, JSON_OPTION_SHORT);
    OPTION_MESSAGE("Print this help", HELP_OPTION, HELP_OPTION_SHORT);
}
int main(int argc, char* argv[]) {
    uint16_t numberOfThreads = DEFAULT_NUMBER_OF_THREADS;
    int iterationsPerThread = DEFAULT_ITERATIONS_PER_THREAD;
    char *outFile = NULL;
    dataFilePath[0] = '\0';
    evidenceFilePath[0] = '\0';
    for (int i = 0; i < argc; i++) {
        if (strcmp(argv[i], DATA_OPTION) == 0 ||
            strcmp(argv[i], DATA_OPTION_SHORT) == 0) {
            
            strcpy(dataFilePath, argv[i + 1]);
        }
        else if (strcmp(argv[i], UA_OPTION) == 0 ||
            strcmp(argv[i], UA_OPTION_SHORT) == 0) {
            
            strcpy(evidenceFilePath, argv[i + 1]);
        }
        else if (strcmp(argv[i], THREAD_OPTION) == 0 ||
            strcmp(argv[i], THREAD_OPTION_SHORT) == 0) {
            
            numberOfThreads = (uint16_t)atoi(argv[i + 1]);
        }
        else if (strcmp(argv[i], JSON_OPTION) == 0 ||
            strcmp(argv[i], JSON_OPTION_SHORT) == 0) {
            
            outFile = argv[i + 1];
        }
        else if (strcmp(argv[i], ITERATIONS_OPTION) == 0 ||
            strcmp(argv[i], ITERATIONS_OPTION_SHORT) == 0) {
            
            iterationsPerThread = atoi(argv[i + 1]);
        }
        else if (strcmp(argv[i], HELP_OPTION) == 0 ||
            strcmp(argv[i], HELP_OPTION_SHORT) == 0) {
            
            printHelp();
            return 0;
        }
        else if (argv[i][0] == '-') {
            
            printf(
                "The option '%s' is not recognized. Use %s (%s) to list options.",
                argv[i],
                HELP_OPTION,
                HELP_OPTION_SHORT);
            return 1;
        }
        else {
            
        }
    }
    if (strlen(dataFilePath) == 0) {
            dataDir,
            dataFileName,
            dataFilePath,
            sizeof(dataFilePath));
            printf(("Failed to find a device detection "
                "data file. Make sure the device-detection-data "
                "submodule has been updated by running "
                "`git submodule update --recursive`\n"));
            fgetc(stdin);
            return 1;
        }
    }
    if (strlen(evidenceFilePath) == 0) {
            dataDir,
            evidenceFileName,
            evidenceFilePath,
            sizeof(evidenceFilePath));
            printf(("Failed to find a device detection "
                "evidence file. Make sure the device-detection-data "
                "submodule has been updated by running "
                "`git submodule update --recursive`\n"));
            fgetc(stdin);
            return 1;
        }
    }
    ExampleParameters params;
    params.dataFilePath = dataFilePath;
    params.evidenceFilePath = evidenceFilePath;
    params.numberOfThreads = numberOfThreads;
    params.iterationsPerThread = iterationsPerThread;
    params.output = stdout;
    if (outFile != NULL) {
        params.resultsOutput = fopen(outFile, "w");
    }
    else {
        params.resultsOutput = NULL;
    }
    
    fiftyoneDegreesExampleMemCheck(
        ¶ms,
        fiftyoneDegreesExampleCPerformanceRun);
    if (outFile != NULL) {
        fclose(params.resultsOutput);
    }
    return 0;
}
#endif