0

I'm trying to write a code that evaluates the run-time of the search algorithm, and I have doubts how to do it correctly. I want to pass a function linear_search to the function code_timer, and code_timer function is expected to evaluate the execution time of the linear search function. In order to solve this problem, I'm trying to use a pointer to a function.

// Search Algorithm function prototype
int *linear_search(int arr[], int arr_size, int val);

// Code timing function that evaluates the performance of the algorithm function
double code_timer( int *arr, int arr_size, int val, int nrun,
        int* (*search_algorithm)(int *arr, int arr_size, int val) )
{
    double interval = 0;
    clock_t start, end;
    int *ptr = NULL;

    start = clock();
    /* Code under Test */
    for (int j = 0; j < nrun; j++) {
        ptr = (*search_algorithm)(arr, arr_size, val);
    }
    /* End of Code under Test */
    end = clock();
    interval = (double)(end - start) / (double)(nrun) / (double) CLOCKS_PER_SEC * 1000000.0;
    return interval; // in microseconds (mcs)
}

Finally I need to call the function code_timer with the function linear_search as an argument.

#define SIZE 100000 //size of data array
#define NRUN 100 // number of runs

int main(void) {
    ...
    // calling code_timer with a function linear_search as a parameter
    linear_result = code_timer(benchmark_data, SIZE, check_point, NRUN, linear_search);
}

I would like to know if I'm doing it correctly, and if no, please advise the way I could do it. Thank you!

Update: My main concern is pointer to function:

  1. Am I declaring it correctly as an argument?
double code_timer( int *arr, int arr_size, int val, int nrun,
        int* (*search_algorithm)(int *arr, int arr_size, int val) );
  1. Am I dereferencing it correctly in code_timer and main functions?
// this is from code_timer function
    for (int j = 0; j < nrun; j++) {
        ptr = (*search_algorithm)(arr, arr_size, val);
    }
// this is from main function
    linear_result = code_timer(benchmark_data, SIZE, check_point, NRUN, linear_search);
alv2017
  • 752
  • 5
  • 14
  • 1
    Why do you not use `typedef` for the function pointers? It would be more readable. – 12431234123412341234123 Sep 10 '20 at 11:55
  • When you call the function pointer, you don't have to write `(*search_algorithm)(...)`. You can just call it like a regular function as `search_algorithm(...)`. – Roy Avidan Sep 10 '20 at 12:04
  • I don't understand this question at all. You're not sure if it *works* ? Well, did it *compile* and *link* without warnings? Did you *test* it? Did you receive the results you expected for your known-answer input data? The logic issue of a null dereference within your function notwithstanding, the actual function pointer usage looks to be correct, but your compiler would/should have already told you that. – WhozCraig Sep 10 '20 at 12:06
  • Yes, I tested, and yes it worked. However I still have doubts, and I would appreciate a feedback from the more experienced C programmers. – alv2017 Sep 10 '20 at 12:09
  • Probably, I have to clear out, that I'm testing multiple algorithms, and the function code_timer has to be able to take as a parameter any function that has the same prototype as the function linear_search does. – alv2017 Sep 10 '20 at 12:11
  • You could ask something more specific about your doubts. Consider using a typedef for the pointer, for readability as @12431234123412341234123 pointed. As for testing with multiple functions for timing it is a common scenario. You can build a vector of pointers and call by index. As for using another function just for testing clone your function and call it – arfneto Sep 10 '20 at 12:23
  • @WhozCraig Just because something works does not mean it is correct and does not cause UB, this is especially true for C where a lot of things work but cause UB. – 12431234123412341234123 Sep 10 '20 at 12:36
  • _Yes, I tested, and yes it worked. However I still have doubts,_ If it works, but you would like to get advice on improving it, post on [Code Review](https://codereview.stackexchange.com/). – ryyker Sep 10 '20 at 12:38
  • @12431234123412341234123 You say that like I'm discounting UB as if it were happenstance, when, if you saw my history on this site, couldn't be further from the truth. The selective case of UB specifically mentioned concerning the null deference of `ptr` already called out, which specific UB in the posted code did you have in mind? This looks like a request for code review; not a question about misbehaving code, and wonderment of the root cause(s), and as such is off-topic for this site. It would be better suited for [code-review](https://codereview.stackexchange.com/) instead. – WhozCraig Sep 10 '20 at 12:40

1 Answers1

2

No this isn't done correctly. One obvious problem is that you can't have printf or any other I/O in the middle of the benchmarking code. It's gonna be the main bottleneck, grabbing all the execution time each lap of the loop. So this code is just benchmarking printf performance + some sugar on top.

Also, printf("Found value: %d\n", *ptr); outside the for loop is strange, surely a bug?

This is what you should do instead:

  • Start the clock
  • Run the function n times in a loop.
  • Store the results in a pre-allocated array.
  • Stop the clock.
  • Print algorithm results.
  • Print benchmarking results.

Preferably use a static array, since dynamic memory access can lead to late OS allocation, meaning the OS might execute the allocation during the first time you actually access the data, rather than at the line where the malloc call is located. And that would screw up benchmarking completely.

Lundin
  • 195,001
  • 40
  • 254
  • 396
  • _"late OS allocation, ,...,OS might execute the allocation during the first time you actually access the data..."_. Would a call to `memset()` right after `malloc()` satisfy to get first call out of the way? – ryyker Sep 10 '20 at 13:31
  • @ryyker Maybe, it would depend on the OS and possibly on the C standard lib implementation of malloc as well. – Lundin Sep 10 '20 at 13:36
  • Sorry! I added printf statements by mistake. Actually I was checking if the function passed by reference gives the correct results, and I forgot to remove it. :-). – alv2017 Sep 10 '20 at 14:21
  • @alv2017 Well it's an entirely different question now... now you are suddenly asking about correct use of function pointers, which isn't really related to benchmarking at all, other than function pointer calls being impossible to inline. – Lundin Sep 10 '20 at 14:27
  • @Lundin Yes, that's right. I'm concerned about the implementation of the function pointer. (Thank you for pointing out my mistake.) – alv2017 Sep 10 '20 at 14:29
  • @alv2017 1) It isn't written with typedef so it is messy by definition. 2) No const correctness, which can affect performance (though unlikely in this case) 3) `(*search_algorithm)(arr ...` is just bloat, simply do `search_algorithm(arr, ...)`. – Lundin Sep 10 '20 at 14:34
  • @Lundin Are you saying that search_algorithm(arr, ...) is equivalent to just search_algorithm(arr, ...) ? What would be the explanation for that? – alv2017 Sep 10 '20 at 14:43
  • @alv2017 This is wildly derailed from your original question. Moving goal posts around when asking questions isn't very polite. Just study function pointer basics. https://stackoverflow.com/questions/840501/how-do-function-pointers-in-c-work and https://stackoverflow.com/questions/6893285/why-do-function-pointer-definitions-work-with-any-number-of-ampersands-or-as – Lundin Sep 10 '20 at 14:49
  • @Lundin Well, maybe it was not very polite, and I apologise about it. However, I upvoted your answer, and then I didn't want to create a new topic, and I just reformulated my question the way I want it to be. – alv2017 Sep 10 '20 at 14:59