Jump to content

C Code review.

So I made a function which benchmarks other functions. Because of my countless failures of achieving generics, I reluctantly had to make different bench functions for different data types. Right now, only the bool version is complete but I will add more functions.

 

I have one question. I am using the same variable names in my union. In my function when I try to access "res", VScode Intellisense shows be something like "bool::res", which probably means it identified that it should use the "res" variable of bool type, because I am taking a bool return type out of the function pointer. But after I return the struct, in my main, if I try to access "res", then which "res" will it choose? I know I've done something wrong.

 

#include <time.h>
#include <stdbool.h>
#include <stdarg.h>

#define RUNS 5
#define AVG_RUNS 5

union result{ //to store the result of the function of any data type because GENERICS SUCKS.
    bool res;
    char res;
    short signed int res;
    signed int res;
    long signed int res;
    long long signed int res;
    short unsigned int res;
    unsigned int res;
    long unsigned int res;
    long long unsigned int res;
    char* res;
};

struct bench{ // We do 5 runs, calculate average and repeat 5 times. Then we calculate the total average of all the 5 averages. We also maintain the highest and lowest.
    // Could have used arrays but who cares.
    long unsigned int run1;
    long unsigned int run2;
    long unsigned int run3;
    long unsigned int run4;
    long unsigned int run5;
    long unsigned int average1;
    long unsigned int average2;
    long unsigned int average3;
    long unsigned int average4;
    long unsigned int average5;
    long unsigned int total_average;
    long unsigned int highest;
    long unsigned int lowest;
};

struct bench_result{ //Our bench function(s) will return this.
    long unsigned int elapsed;
    union result result;
};



inline struct bench_result bench_bool(bool (*funcPtr)(va_list), ...) //Only works on boolean functions. Will add more functions which use other data types. All this because GENERICS SUCKS!
{
    struct timespec start, end; //to measure time
    va_list args; //to pass the args to the function.
    va_start(args, funcPtr);
    struct bench bench;
    union result result;
    long unsigned int* runP = &bench.run1;      //to iterate over the variables.
    long unsigned int* avgP = &bench.average1;    
    long unsigned int avg_accumulator = 0ULL; //accumulator to add the values for calculating average
    for (unsigned char i = 0; i < AVG_RUNS; i++)
    {
        for (unsigned char j = 0; j < RUNS; j++)
        {
            clock_gettime(CLOCK_MONOTONIC, &start);
            result.res = funcPtr(args); //notice, we are storing the the result in our union
            clock_gettime(CLOCK_MONOTONIC, &end);
            *runP = end.tv_nsec - start.tv_nsec; //storing the elapsed time in the current run
            avg_accumulator += *runP; //adding the time to the accumulator
            runP++; //update pointer
        }
        *avgP = avg_accumulator / RUNS; //calculating average
        avg_accumulator = 0ULL; //resetting accumulator
        avgP++; //update pointer
        //long ass convuluted ternaries for calculating highest and lowest
        long unsigned int highest_current = (bench.run1 > bench.run2 ? (bench.run1 > bench.run3 ? (bench.run1 > bench.run4 ? (bench.run1 > bench.run5 ? bench.run1 : bench.run5) : (bench.run4 > bench.run5 ? bench.run4 : bench.run5)) : (bench.run3 > bench.run4 ? (bench.run3 > bench.run5 ? bench.run3 : bench.run5) : (bench.run4 > bench.run5 ? bench.run4 : bench.run5))) : (bench.run2 > bench.run3 ? (bench.run2 > bench.run4 ? (bench.run2 > bench.run5 ? bench.run2 : bench.run5) : (bench.run4 > bench.run5 ? bench.run4 : bench.run5)) : (bench.run3 > bench.run4 ? (bench.run3 > bench.run5 ? bench.run3 : bench.run5) : (bench.run4 > bench.run5 ? bench.run4 : bench.run5))));
        bench.highest = highest_current > bench.highest ? highest_current : bench.highest;
        long unsigned int lowest_current = (bench.run1 < bench.run2 ? (bench.run1 < bench.run3 ? (bench.run1 < bench.run4 ? (bench.run1 < bench.run5 ? bench.run1 : bench.run5) : (bench.run4 < bench.run5 ? bench.run4 : bench.run5)) : (bench.run3 < bench.run4 ? (bench.run3 < bench.run5 ? bench.run3 : bench.run5) : (bench.run4 < bench.run5 ? bench.run4 : bench.run5))) : (bench.run2 < bench.run3 ? (bench.run2 < bench.run4 ? (bench.run2 < bench.run5 ? bench.run2 : bench.run5) : (bench.run4 < bench.run5 ? bench.run4 : bench.run5)) : (bench.run3 < bench.run4 ? (bench.run3 < bench.run5 ? bench.run3 : bench.run5) : (bench.run4 < bench.run5 ? bench.run4 : bench.run5))));
        bench.lowest = lowest_current < bench.lowest ? lowest_current : bench.lowest;
        runP = &bench.run1;
    }
    bench.total_average = (bench.average1 + bench.average2 + bench.average3 + bench.average4 + bench.average5) / AVG_RUNS; //calculate the final avetage
    struct bench_result res;
    res.elapsed = bench.total_average;
    res.result = result; //passing the union itself
    va_end(args);
    return res;
}

 

Also, is it hard for you guys to read code on this forum? If so, I might post the code on some other website. It's shocking I don't have a GitHub account yet.

Microsoft owns my soul.

 

Also, Dell is evil, but HP kinda nice.

Link to comment
Share on other sites

Link to post
Share on other sites

1 hour ago, Gat Pelsinger said:

I have one question. I am using the same variable names in my union. In my function when I try to access "res", VScode Intellisense shows be something like "bool::res", which probably means it identified that it should use the "res" variable of bool type, because I am taking a bool return type out of the function pointer. But after I return the struct, in my main, if I try to access "res", then which "res" will it choose? I know I've done something wrong.

I'm too lazy to look up the full definitions in the C99 or later standards, but I'd imagine that it's undefined behavior (i.e. it all depends on what the compiler chooses at the time).

 

With unions you shouldn't be reusing names as there isn't any method to properly tell the compiler which option you wish to use on the given line.

 

 

 

Another thing to note, while I do use timers to get a gauge on my functions from time to time; ultimately you need to run something like a profiler to properly gauge slowdowns (and areas you might consider optimizing). And in some aspects the "slow" function that is identified could be made faster by optimizations to the data that is passed into the function (vs trying to optimize the function itself).

 

Finally another note, measuring time for a single function to run isn't a good approach, especially if you only do it 5 times (well I mean unless the function takes a long time to run)...you effectively introduce different types of errors in your measuring.

 

e.g.  If you have a function like bool test(int a) { return a > 0; }...this function runs insanely fast...but occasionally you will get an interupt by the OS or some other thing will prevent the execution for a few cycles.  This will then result in getting a really high number (even though it's not).

 

Even on some pieces of code I have tried using a method like you have; I have a 10 second runtime...but other times I will get 9 seconds this is despite it running on the same data.  So while if I optimize it and consistently get 9 seconds now, it's harder to conclude if I actually made much of a difference.

3735928559 - Beware of the dead beef

Link to comment
Share on other sites

Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

×