decreasing time it takes to run my program in c - c

I was writing a program that is reading from a file and then storing the data in two tables that are in a table of structure. I am expanding the tables with realloc and the time my program takes to run is ~ 0.7 s.
Can i somehow decrease this time?
typedef struct {
int *node;
int l;
int *waga;
} przejscie_t;
void czytaj(przejscie_t **graf, int vp, int vk, int waga) {
(*graf)[vp].node[(*graf)[vp].l - 1] = vk;
(*graf)[vp].waga[(*graf)[vp].l - 1] = waga;
(*graf)[vp].l++;
}
void wypisz(przejscie_t *graf, int i) {
printf("i=%d l=%d ", i, graf[i].l);
for (int j = 0; j < (graf[i].l - 1); j++) {
printf("vk=%d waga=%d ", graf[i].node[j], graf[i].waga[j]);
}
printf("\n");
}
void init(przejscie_t **graf, int vp, int n) {
*graf = realloc(*graf, (vp + 1) * sizeof(przejscie_t));
if (n == vp || n == -1){
(*graf)[vp].l = 1;
(*graf)[vp].node = malloc((*graf)[vp].l * sizeof(int));
(*graf)[vp].waga = malloc((*graf)[vp].l * sizeof(int));
}
else {
for (int i = n; i <= vp; i++) {
(*graf)[i].l = 1;
(*graf)[i].node = malloc((*graf)[i].l * sizeof(int));
(*graf)[i].waga = malloc((*graf)[i].l * sizeof(int));
}
}
}

Here some suggestions:
I think you should pre-calculate the required size of your *graf memory instead of reallocating it again and again. By using a prealloc_graf function for example.
You will get some great time improvement since reallocating is time-consuming especially when it must actually move the memory.
You should do this method especially if you are working with big files.
And since you're working with files, pre-calculating should be done easily.
If your files size are both light and heavy, you have two choices:
Accept your fate and allow your code to be a little bit less optimized on small files.
Create two init functions: The first one is optimized for small files, the other one will be for bigger files but... You will have to run some benchmarks to actually determine what algorithm is the best for each case before being able to implement it. You could actually automate that if you have the time and the will to do so.
It is important to check for successful memory allocation before trying to use the said memory because allocation function can fail.
Finally, some changes for the init function :
void init(przejscie_t **graf, int vp, int n) {
*graf = realloc(*graf, (vp + 1) * sizeof(przejscie_t));
// The `if` statement was redundant.
// Added a ternary operator for ``n == -1``.
// Alternatively, you could use ``n = (n == -1 ? vp : n)`` right before the loop.
for (int i = (n == -1 ? vp : n); i <= vp; i++) {
(*graf)[i].l = 1;
// (*graf)[X].l is is always 1.
// There is no reason to use (*graf)[X].l * sizeof(int) for malloc.
(*graf)[i].node = malloc(sizeof(int));
(*graf)[i].waga = malloc(sizeof(int));
}
}
I've commented everything that I've changed but here is a summary :
The if statement was redundant.
The for loop cover all cases with ternary operator for n
equals -1.
The code should be easier to understand and to comprehend this way.
The node and waga arrays were not being initialized "properly".
Since l is always equals 1 there was no need for an
additional operation.
This doesn't really change execution time tho since its constant.
I would also suggest that your "functions running allocation functions" should return a boolean saying if the function succeeded. In the case the allocation failed you can return false to say that your function failed.

Related

Any way to avoid warning C6386, without disabling it or Code Analysis altogether

Visual Studio 2019 started showing Code Analysis warnings as in-editor green squiggles by default. These may be extremely useful for students learning C programming, because they catch classical mistakes, such as off by one array accesses.
Unfortunately false positives may completely ruin the learning experience and I fear that I will have to ask the students to disable the feature in order to avoid having them worry on non existing problems.
This short snippet doesn't cause any warning:
#include <stdlib.h>
int main(void)
{
size_t n = 6;
int *v = malloc(n * sizeof(int));
if (v == NULL) {
return 1;
}
for (size_t i = 0; i < n; ++i) {
v[i] = i;
}
free(v);
return 0;
}
Unfortunately, if you move the allocation in a function, like this:
#include <stdlib.h>
int *test(size_t n)
{
int *v = malloc(n * sizeof(int));
if (v == NULL) {
return NULL;
}
for (size_t i = 0; i < n; ++i) {
v[i] = i;
}
return v;
}
int main(void)
{
size_t n = 6;
int *v = test(n);
free(v);
return 0;
}
you get a warning C6386: Buffer overrun while writing to 'v': the writable size is 'n*sizeof(int)' bytes, but '8' bytes might be written.
Even reading on Stack Overflow, I don't get where the '8' comes from, but, more importantly, why it fails to recognize that i will never be out of range.
So the question is: is there a way to write this type of code in a way that will not generate the warning?
I know that I can go to Tools > Options > Text Editor > C/C++ > Experimental > Code Analysis and set Disable Code Analysis Squiggles to True, or use a #pragma warning(disable:6386), but I'd rather avoid it, and certainly avoid suggesting my students the latter.
I really want to thank everybody for their contributions and I agree that it is a bug in the Code Analyzer (by looking on Microsoft web sites it has been "Closed - Lower Priority" two years ago...).
Adrian Mole max(n, 0) trick points to a way for coping with the warning in code, that is checking that n is greater than zero. The funny thing is that you can still use that zero for what n was supposed to be used. While the idea could be used for experienced programmers (that would probably disable the warning), as John Bollinger points out, it's not for students.
So, after telling the students that it's a bug and how to turn off the Code Analysis squiggles or disable the warning, I'd go with
int *test(size_t n)
{
if (n == 0) {
return NULL;
}
int *v = malloc(n * sizeof(int));
if (v == NULL) {
return NULL;
}
for (size_t i = 0; i < n; ++i) {
v[i] = i;
}
return v;
}
Which may also be interpreted as: don't allow 0 elements allocation.
You can supress this warning (which could be considered a bug), simply by ensuring the value of n given to the malloc call has not "wrapped around" following overflow (as hinted at in the comment from Eric Postpischill).
To do this, you can replace the n argument by the seemingly bizarre max(n,0):
int* test(size_t n)
{
// int* v = malloc(n * sizeof(int)); // Warning C6386 on v[i] = i
int* v = malloc(max(n, 0) * sizeof(int)); // No warning
if (v == NULL) {
return NULL;
}
for (size_t i = 0; i < n; ++i) {
v[i] = i;
}
return v;
}

C segmentation fault

I'm trying to create a sub array with the following function :
Track * subArray(Track * arr, int start, int end){
int size = end - start;
Track * t = malloc(sizeof(Track) * size);
for(int i = 0; i < size && start <= end; i++){
t[i] = arr[start++];
}
}
The size of the t pointer is always 8, even when I don't multiply it with size and I get a segmentation fault. I'm new to C so I don't know what is causing this exception.
This is why C is hard. It's an off by one error: You need to allocate (end-start+1) items and use <= in the loop. Try rewriting to something like this:
#include <assert.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
typedef struct Track {
char* color;
} Track;
Track * subArray(Track* arr, int start, int end){
assert(end > start);
const int size = 1 + end - start;
printf("Allocating %d items\n", size);
Track* t = malloc(sizeof(Track)*size);
for(int i=start; i <= end; ++i) {
printf("at %d fetching %d\n", i-start, i);
t[i-start] = arr[i];
}
return t;
}
int main() {
Track *track = malloc(sizeof(Track) * 7);
track[0].color = "red";
track[1].color = "orange";
track[2].color = "yellow";
track[3].color = "blue";
track[4].color = "indigo";
track[5].color = "green";
track[6].color = "violet";
Track *sub = subArray(track, 3, 5);
printf("%s\n", sub[0].color);
printf("%s\n", sub[1].color);
printf("%s\n", sub[2].color);
}
Compiling and running:
$ cc -g -W -Wall a.c && ./a.out
Allocating 3 items
at 0 fetching 3
at 1 fetching 4
at 2 fetching 5
blue
indigo
green
Note that I'm copying the value of char* pointers here. That may lead to additional confusing stuff, just in case you think about copying my code (I just drafted something that works to illustrate the problem).
Update
You're using inclusive indices. In C, however, it's quite common to specify a start index and a length. A lot of standard library functions do this, and this is what you'll most likely see in production code. One reason may be that it's easier to reason with. In your case, the code would be
Track* subArray(Track* arr, const size_t start, const size_t length) {
Track* t = malloc(sizeof(Track) * length);
for (size_t i = 0; i < length; ++i)
t[i] = arr[i + start];
return t;
}
and the corresponding call would be
Track *sub = subArray(track, 3, 3);
In my eyes, this not only looks better; it's simpler and easier to understand.
Another thing that's common is to copy pointers instead of the whole structs. This will depend on how your code and data structures are organized. In that case, it's quite common to use a sentry value at the end of an array of pointers to mark its end: This will typically be a NULL pointer.
Keep practising and keep reading other people's code, and you'll soon discover C idioms and programming styles that will make your life much easier!
I think your error is that you're using <= in your tests when they should be <. This will prevent you from running off the end of your arrays.

Value changes in a struct when editing another struct in another function

I've run into a problem with structs which I believe is caused by incorrect mallocs, or possibly rather my reallocs. I've cut down the code as much as possible to only show what I believe could be relevant, so nearly all the actual operations are omitted.
The struct I have looks as follows:
struct poly_t {
int nvars, *factor, *exp;
};
The value that's acting weird is nvars, which to me would signify that I'm somehow not reserving memory properly. What I do is that I first have a function that creates and fills the struct, then I have another function where I send two of these structs in and create a third identical struct. When editing the value of nvars in this third struct, it very rarely also edits the value of nvars in the first struct. When running gdb, it shows the exact row it happens on is when I do thirdp->nvars++; in my mul function.
So with this function I create my first and second structs (a and b).
poly_t* new_poly_from_string(const char* a){
struct poly_t* p = malloc(sizeof(struct poly_t));
p->nvars = 0;
p->factor = malloc(strlen(a) * sizeof(int));
p->exp = malloc(strlen(a) * sizeof(int));
for (int i = 0; i < strlen(a); i++){
//do stuff to put a into p, using at most p->factor[p->nvars] and the same for p->exp
p->factor = realloc(p->factor, p->nvars*sizeof(int));
p->exp = realloc(p->exp, p->nvars*sizeof(int));
printf("%d", p->nvars); //At this point, nvars is the correct value
return p;
}
And below is the function that works in 9/10 cases but in some rare cases it doesn't. I've marked the exact row that changes a->nvars with an arrow -->.
poly_t* mul(poly_t* a, poly_t* b){
struct poly_t* thirdp = malloc(sizeof(struct poly_t));
thirdp->nvars = 0;
thirdp->factor = malloc((a->nvars + b->nvars) * sizeof(int));
thirdp->exp = malloc((a->nvars + b->nvars) * sizeof(int));
for (int i = 0; i < a->nvars; i++){
for (int j = 0; j < b->nvars; j++){
for (int k = 0; k < p->nvars; k++){
if (p->exp[k] == a->exp[i] + b->exp[j]){
p->factor[k] += a->factor[i]*b->factor[j];
found = 1;
break;
}
}
if (!found){
p->factor[p->nvars] = a->factor[i]*b->factor[j];
p->exp[p->nvars] = a->exp[i] + b->exp[j];
--> p->nvars++; //This is the row that changes a->nvars according to gdb
}
}
}
return thirdp;
}
Here's what I got when running gdb while trying to figure out what was changing a->nvars. Note that p is the same as thirdp above, I just renamed it here for clarity.
edit: Readded the actual code in the mul function
You are allocating space for the sum of the number of integers
a->nvars + b->nvars
I wonder what you do in
//fill thirdp->factor and thirdp->exp
As you have nested for loops I suspect you may be generating
a->nvars * b->nvars //multiply
items and hence running off the end of allocated space. You now show us the code and we see
if (!found){
// --- here ----
p->factor[p->nvars] = a->factor[i]*b->factor[j];
p->exp[p->nvars] = a->exp[i] + b->exp[j];
p->nvars++;
}
At the point I mark here you should check the value of p->nvars, I think it has the possibility to reach a value greater than (a->nvars + b->nvars).
I think to be safe allocate the space for (a->nvars * b->nvars) ints.
The problem was as I believed in the mallocs. Specifically the following two rows:
thirdp->factor = malloc((a->nvars + b->nvars) * sizeof(int));
thirdp->exp = malloc((a->nvars + b->nvars) * sizeof(int));
I simply wasn't allocating enough memory which caused the weird behaviour. What I wanted was:
thirdp->factor = malloc((a->exp[0] + b->exp[0]) * sizeof(int));
thirdp->exp = malloc((a->exp[0] + b->exp[0]) * sizeof(int));
Unfortunately since I didn't give any of the indata, or really much context, you guys probably couldn't have figured that out <.< Sorry!

Malloc affecting random integer value

I'm writing a virtual memory simulator in C, compiling on linux, and I'm getting something rather strange. It takes in a file IO, which I put into an int* plist.
I've printed this "plist" array, and it comes out to
0 100
1 200
2 400
3 300
etc
The problem is that it seems malloc or something is randomly changing plist[3] to 0. It doesn't seem like it should be that way, but I've put a print statement at every line of code to print plist[3], and
tables[i].valid = (char*) xmalloc(num_pages * sizeof(char));
is where it changes. plist[3] = 300 before the line, 0 after it. And it only does this when i = 2. The first 3 rounds of the loop run fine, and on round 3, it changes the values for round 4. I have no idea why, it makes little sense that malloc would change a value in an array that's completely unrelated - is it possible I've gone over some space limit, even though I'm using the heap for basically everything? Would it just change values in random arrays if I did?
for(i = 0; i < 4; i++){
num_pages = plist[i] / P1;
tables[i].page_num = (char**) xmalloc(num_pages * sizeof(char*));
tables[i].valid = (char*) xmalloc(num_pages * sizeof(char));
//initialize page numbers and valid bits
for(j = 0; j < 10; j++){
tables[i].page_num[j] = (char*) xmalloc(16*sizeof(char));
tmp = itoa(i, tmp);
strcat(tables[i].page_num[j], tmp);
strcat(tables[i].page_num[j], "p");
tmp = itoa(j, tmp);
strcat(tables[i].page_num[j], tmp);
tables[i].valid[j] = 0;
}
}
Here's the struct for tables:
typedef struct s_page_table
{
char** page_num;
char* valid;
} t_page_table;
And this is xmalloc (it's just a wrapper to make it easier):
void* xmalloc(int s)
{
void* p;
p = malloc(s);
if (p == NULL)
{
printf("Virtual Memory Exhausted");
exit(1);
}
return p;
}
EDIT: If I take out both lines referencing tables[i].valid, the problem does not exist. plist[3] stays the same. num_pages is always >= 10. I set j to be 0 to 10 just to have less output for debugging purposes.
EDIT 2: If I change valid from a char* to an int* it doesn't work. If I change it to an int, it does.
There are several possibilities, including (but not limited to):
tables[i] is out of bounds;
plist contains a dangling pointer (i.e. it's been deallocated);
plist hasn't been initialised;
plist isn't as large as you think, i.e. plist[3] is out of bounds.
If you can't figure out the problem by looking at the code, valgrind is your friend.
OK. So I believe the problem turned out to be playing with the strings before initializing everything. I'm not entirely certain the reason, maybe someone else can elaborate, but when I encapsulated JUST the initialization in its own function, like only doing mallocs, and then separately created the strings afterwards, the plist variable was unaffected.
For those interested, the encapsulated function looked like this:
t_page_table* table_builder(int* p, int x, int num_tables)
{
t_page_table* ret = xmalloc(num_tables * sizeof(*ret));
int i, tmp, j;
for(i = 0; i < num_tables; i++){
tmp = (p[i]/x);
ret[i].page_num = xmalloc(tmp * sizeof(char*));
ret[i].valid = xmalloc(tmp * sizeof(char));
for(j = 0; j < tmp; j++){
ret[i].page_num[j] = xmalloc(16 * sizeof(char));
ret[i].valid = 0;
}
}
return ret;
}

longest common subsequence: why is this wrong?

int lcs(char * A, char * B)
{
int m = strlen(A);
int n = strlen(B);
int *X = malloc(m * sizeof(int));
int *Y = malloc(n * sizeof(int));
int i;
int j;
for (i = m; i >= 0; i--)
{
for (j = n; j >= 0; j--)
{
if (A[i] == '\0' || B[j] == '\0')
X[j] = 0;
else if (A[i] == B[j])
X[j] = 1 + Y[j+1];
else
X[j] = max(Y[j], X[j+1]);
}
Y = X;
}
return X[0];
}
This works, but valgrind complains loudly about invalid reads. How was I messing up the memory? Sorry, I always fail at C memory allocation.
The issue here is with the size of your table. Note that you're allocating space as
int *X = malloc(m * sizeof(int));
int *Y = malloc(n * sizeof(int));
However, you are using indices 0 ... m and 0 ... n, which means that there are m + 1 slots necessary in X and n + 1 slots necessary in Y.
Try changing this to read
int *X = malloc((m + 1) * sizeof(int));
int *Y = malloc((n + 1) * sizeof(int));
Hope this helps!
Series of issues. First, as templatetypedef says, you're under-allocated.
Then, as paddy says, you're not freeing up your malloc'd memory. If you need the Y=X line, you'll need to store the original malloc'd space addresses in another set of variables so you can call free on them.
...mallocs...
int * original_y = Y;
int * original_x = X;
...body of code...
free(original_y);
free(original_x);
return X[0];
But this doesn't address your new question, which is why doesn't the code actually work?
I admit I can't follow your code (without a lot more study), but I can propose an algorithm that will work and be far more understandable. This may be somewhat pseudocode and not particularly efficient, but getting it correct is the first step. I've listed some optimizations later.
int lcs(char * A, char * B)
{
int length_a = strlen(A);
int length_b = strlen(B);
// these hold the position in A of the longest common substring
int longest_found_length = 0;
// go through each substring of one of the strings (doesn't matter which, you could pick the shorter one if you want)
char * candidate_substring = malloc(sizeof(char) * length_a + 1);
for (int start_position = 0; start_position < length_a; start_position++) {
for (int end_position = start_position; end_position < length_a; end_position++) {
int substring_length = end_position - start_position + 1;
// make a null-terminated copy of the substring to look for in the other string
strncpy(candidate_substring, &(A[start_position]), substring_length);
if (strstr(B, candidate_substring) != NULL) {
longest_found_length = substring_length;
}
}
}
free(candidate_substring);
return longest_found_length;
}
Some different optimizations you could do:
// if this can't be longer, then don't bother checking it. You can play games with the for loop to not have this happen, but it's more complicated.
if (substring_length <= longest_found_index) {
continue;
}
and
// there are more optimizations you could do to this, but don't check
// the substring if it's longer than b, since b can't contain it.
if (substring_length > length_b) {
continue;
}
and
if (strstr(B, candidate_substring) != NULL) {
longest_found_length = end_position - start_position + 1;
} else {
// if nothing contains the shorter string, then nothing can contain the longer one, so skip checking longer strings with the same starting character
break; // skip out of inner loop to next iteration of start_position
}
Instead of copying each candidate substring to a new string, you could do a character swap with the end_position + 1 and a NUL character. Then, after looking for that substring in b, swap the original character at end_position+1 back in. This would be much faster, but complicates the implementation a little.
NOTE: I don't normally write two answers and if you feel that it is tacky, feel free to comment on this one and note vote it up. This answer is a more optimized solution, but I wanted to give the most straightforward one I could think of first and then put this in another answer to not confuse the two. Basically they are for different audiences.
The key to solving this problem efficiently is to not throw away information you have about shorter common substrings when looking for longer ones. Naively, you check each substring against the other one, but if you know that "AB" matches in "ABC", and your next character is C, don't check to see if "ABC" is in "ABC", just check that the spot after "AB" is a "C".
For each character in A, you have to check up to all the letters in B, but because we stop looking through B once a longer substring is no longer possible, it greatly limits the number of checks. Each time you get a longer match up front, you eliminate checks on the back-end, because it will no longer be a longer substring.
For example, if A and B are both long, but contain no common letters, each letter in A will be compared against each letter in B for a runtime of A*B.
For a sequence where there are a lot of matches, but the match length isn't a large fraction of the length of the shorter string, you have A * B combinations to check against the shorter of the two strings (A or B) leading to either A*B*A or A*B*B, which is basically O(n^3) time for similar length strings. I really thought the optimizations in this solution would be better than n^3 even though there are triple-nested for loops, but it appears to not be as best as I can tell.
I'm thinking about this some more, though. Either the substrings being found are NOT a significant fraction of the length of the strings, in which case the optimizations don't do much, but the comparisons for each combination of A*B don't scale with A or B and drop out to be constants -- OR -- they are a significant fraction of A and B and it directly divides against the A*B combinations that have to be compared.
I just may ask this in a question.
int lcs(char * A, char * B)
{
int length_a = strlen(A);
int length_b = strlen(B);
// these hold the position in A of the longest common substring
int longest_length_found = 0;
// for each character in one string (doesn't matter which), look for incrementally larger strings in the other
for (int a_index = 0; a_index < length_a - longest_length_found; a_index++) {
for (int b_index = 0; b_index < length_b - longest_length_found; b_index++) {
// offset into each string until end of string or non-matching character is found
for (int offset = 0; A[a_index+offset] != '\0' && B[b_index+offset] != '\0' && A[a_index+offset] == B[b_index+offset]; offset++) {
longest_length_found = longest_length_found > offset ? longest_length_found : offset;
}
}
}
return longest_found_length;
}
In addition to what templatetypedef said, some things to think about:
Why aren't X and Y the same size?
Why are you doing Y = X? That's an assignment of pointers. Did you perhaps mean memcpy(Y, X, (n+1)*sizeof(int))?

Resources