Skip to content

Commit 6b8a586

Browse files
authored
Fix memory leaks and update benchmarks (#22)
2 parents 6c82f5d + d0a9d2e commit 6b8a586

12 files changed

+98
-56
lines changed

benchmark/README.md

+25-23
Original file line numberDiff line numberDiff line change
@@ -2,50 +2,52 @@
22

33
This directory contains the benchmarking code for the project. The benchmarking code is written in Python and uses the `timeit` module to measure the time taken to execute the same code written in Python and WordLanguage. (see [benchmark.py](benchmark.py))
44

5+
Benckmark with version 0.0.8.
6+
57
## Output
68

79
On my pc:
810
```
911
Running benchmark for 'if' scripts
10-
Python: 33.488615 s for 1000 iterations (avg: 0.033489 s/iter)
11-
Word: 3.441121 s for 1000 iterations (avg: 0.003441 s/iter)
12-
Ratio python/word: 9.73x
12+
Python: 34.030275 s for 1000 iterations (avg: 0.03403 s/iter)
13+
Word: 3.659287 s for 1000 iterations (avg: 0.003659 s/iter)
14+
Ratio python/word: 9.3x
1315
1416
Running benchmark for 'while' scripts
15-
Python: 33.493601 s for 1000 iterations (avg: 0.033494 s/iter)
16-
Word: 3.35622 s for 1000 iterations (avg: 0.003356 s/iter)
17-
Ratio python/word: 9.98x
17+
Python: 35.609537 s for 1000 iterations (avg: 0.03561 s/iter)
18+
Word: 3.772758 s for 1000 iterations (avg: 0.003773 s/iter)
19+
Ratio python/word: 9.44x
1820
1921
Running benchmark for 'fibonacci_10' scripts
20-
Python: 33.592082 s for 1000 iterations (avg: 0.033592 s/iter)
21-
Word: 4.506011 s for 1000 iterations (avg: 0.004506 s/iter)
22-
Ratio python/word: 7.45x
22+
Python: 35.488974 s for 1000 iterations (avg: 0.035489 s/iter)
23+
Word: 4.759803 s for 1000 iterations (avg: 0.00476 s/iter)
24+
Ratio python/word: 7.46x
2325
2426
Running benchmark for 'fibonacci_20' scripts
25-
Python: 34.694974 s for 1000 iterations (avg: 0.034695 s/iter)
26-
Word: 76.360572 s for 1000 iterations (avg: 0.076361 s/iter)
27-
Ratio python/word: 0.45x
27+
Python: 36.500882 s for 1000 iterations (avg: 0.036501 s/iter)
28+
Word: 87.358467 s for 1000 iterations (avg: 0.087358 s/iter)
29+
Ratio python/word: 0.42x
2830
```
2931

3032
On Github Actions:
3133
```
3234
Running benchmark for 'if' scripts
33-
Python: 2.118754 s for 100 iterations (avg: 0.021188 s/iter)
34-
Word: 0.391955 s for 100 iterations (avg: 0.00392 s/iter)
35-
Ratio python/word: 5.41x
35+
Python: 1.955134 s for 100 iterations (avg: 0.019551 s/iter)
36+
Word: 0.384222 s for 100 iterations (avg: 0.003842 s/iter)
37+
Ratio python/word: 5.09x
3638
3739
Running benchmark for 'while' scripts
38-
Python: 2.076555 s for 100 iterations (avg: 0.020766 s/iter)
39-
Word: 0.387519 s for 100 iterations (avg: 0.003875 s/iter)
40-
Ratio python/word: 5.36x
40+
Python: 1.947458 s for 100 iterations (avg: 0.019475 s/iter)
41+
Word: 0.380653 s for 100 iterations (avg: 0.003807 s/iter)
42+
Ratio python/word: 5.12x
4143
4244
Running benchmark for 'fibonacci_10' scripts
43-
Python: 2.110027 s for 100 iterations (avg: 0.0211 s/iter)
44-
Word: 0.549725 s for 100 iterations (avg: 0.005497 s/iter)
45-
Ratio python/word: 3.84x
45+
Python: 1.973353 s for 100 iterations (avg: 0.019734 s/iter)
46+
Word: 0.518058 s for 100 iterations (avg: 0.005181 s/iter)
47+
Ratio python/word: 3.81x
4648
4749
Running benchmark for 'fibonacci_20' scripts
48-
Python: 2.279345 s for 100 iterations (avg: 0.022793 s/iter)
49-
Word: 13.906515 s for 100 iterations (avg: 0.139065 s/iter)
50+
Python: 2.144943 s for 100 iterations (avg: 0.021449 s/iter)
51+
Word: 13.806171 s for 100 iterations (avg: 0.138062 s/iter)
5052
Ratio python/word: 0.16x
5153
```

bin/test.w

+1-6
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,3 @@
1-
int a assign 0
21
infloop
3-
if a equal 1000000
4-
break
5-
endif
6-
change a to a plus 1
2+
break
73
endinf
8-
print a

bin/word.exe

512 Bytes
Binary file not shown.

include/interpreter.h

+4
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,10 @@
99
#include "scope.h"
1010
#include "w_stdlib.h"
1111

12+
#ifndef DEBUG
13+
#define DEBUG false
14+
#endif
15+
1216
char *type_keywords[] = {
1317
"null", "int", "float", "str", "bool", "list",
1418
};

include/w_alloc.h

+2
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,8 @@
1212

1313
#if MONITOR_MEMORY
1414

15+
#define DEBUG true
16+
1517
extern int w_alloc_count;
1618
extern long long int w_alloc_total;
1719

src/interpreter.c

+26-19
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,20 @@
11
#include "interpreter.h"
2-
#define DEBUG false
32

43
int main(int argc, char *argv[]) {
54

6-
if (argc < 2 && !DEBUG) {
7-
printf("Usage: word.exe <path>\n");
8-
return 1;
9-
}
10-
11-
if (strlen(argv[1]) < 3) {
12-
fprintf(stderr, "Error: Invalid file name\n");
13-
return 1;
14-
}
15-
if (strcmp(argv[1] + strlen(argv[1]) - 2, ".w") != 0) {
16-
fprintf(stderr, "Error: Invalid file extension\n");
17-
return 1;
5+
if (!DEBUG) { //file name checks
6+
if (argc < 2) {
7+
printf("Usage: word.exe <path>\n");
8+
return 1;
9+
}
10+
if (strlen(argv[1]) < 3) {
11+
fprintf(stderr, "Error: Invalid file name\n");
12+
return 1;
13+
}
14+
if (strcmp(argv[1] + strlen(argv[1]) - 2, ".w") != 0) {
15+
fprintf(stderr, "Error: Invalid file extension\n");
16+
return 1;
17+
}
1818
}
1919

2020
FILE *file;
@@ -35,8 +35,6 @@ int main(int argc, char *argv[]) {
3535
list *parsed_code = parse(lexed_code);
3636
if (DEBUG) print_parsed_code(parsed_code); //DEBUG
3737

38-
if (MONITOR_MEMORY) w_alloc_print();
39-
4038
//initialize variables
4139
if (DEBUG) printf("Initializing main scope...\n"); //DEBUG
4240
Scope *main_scope = init_scope();
@@ -52,7 +50,7 @@ int main(int argc, char *argv[]) {
5250
if (DEBUG) printf("Executing...\n");
5351
execute(parsed_code, main_scope, return_type, true);
5452
if (DEBUG) printf("Executed !\n");
55-
// parser_destroy(parsed_code); // TODO: Fix double free, not necessary but cleaner (memory leak)
53+
parser_destroy(parsed_code);
5654

5755
if (DEBUG) printf("Done\n"); //DEBUG
5856
if (MONITOR_MEMORY) w_alloc_print();
@@ -119,6 +117,7 @@ void *execute(list *parsed_code, Scope *scope, W_Type return_type, bool destroy_
119117
if (strcmp(word->value, "def") == 0) { //SECTION - def
120118
statement = "function definition";
121119

120+
if (DEBUG) printf("Defining function...\n");
122121
W_Func *f = w_func_init(); //create function
123122
dict *fn_args = f->args; //create arguments dictionary
124123

@@ -221,7 +220,8 @@ void *execute(list *parsed_code, Scope *scope, W_Type return_type, bool destroy_
221220
fprintf(stderr, "Error: Variable '%s' already exists, line: %d\n", name, word->line);
222221
exit(1);
223222
}
224-
w_dict_set(scope->vars, name, f); //!SECTION - def
223+
w_dict_set(scope->vars, name, f);
224+
if (DEBUG) printf("Function defined !\n"); //!SECTION - def
225225

226226
} else if (strcmp(word->value, "enddef") == 0) {
227227
statement = "enddef";
@@ -353,6 +353,7 @@ void *execute(list *parsed_code, Scope *scope, W_Type return_type, bool destroy_
353353
char *str = w_dict_stringify(scope->vars);
354354
printf("%s\n", str);
355355
w_free(str);
356+
printf("Initializing function scope...\n");
356357
}
357358

358359
Scope *fn_scope = init_scope(); //function scope
@@ -432,6 +433,7 @@ void *execute(list *parsed_code, Scope *scope, W_Type return_type, bool destroy_
432433
exit(1);
433434
}
434435
if (DEBUG) {
436+
printf("Function scope initialized !\n");
435437
printf("Function variables: (%p)\n", fn_vars); //DEBUG
436438
char *str = w_dict_stringify(fn_vars);
437439
printf("%s\n", str);
@@ -460,15 +462,17 @@ void *execute(list *parsed_code, Scope *scope, W_Type return_type, bool destroy_
460462
}
461463
} else {
462464
if (DEBUG) printf("Starting function execution...\n");
463-
execute(f->parsed_code, fn_scope, f->return_type, true);
465+
void *result = execute(f->parsed_code, fn_scope, f->return_type, true);
466+
if (result != NULL) ((W_Var *)result)->destroy(result);
464467
}
465468
if (DEBUG) printf("Function executed !\n");
466469
} else if (nb_args > 0) {
467470
fprintf(stderr, "Error: Expected keyword 'with' and %d argument(s) to call function '%s', line: %d\n", nb_args, name, word->line);
468471
exit(1);
469472
} else {
470473
if (DEBUG) printf("Starting function execution...\n");
471-
execute(f->parsed_code, fn_scope, f->return_type, true);
474+
void *result = execute(f->parsed_code, fn_scope, f->return_type, true);
475+
if (result != NULL) ((W_Var *)result)->destroy(result);
472476
if (DEBUG) printf("Function executed !\n");
473477
} //!SECTION - call
474478
}
@@ -556,6 +560,7 @@ void *execute(list *parsed_code, Scope *scope, W_Type return_type, bool destroy_
556560
if (return_value != NULL) {
557561
list_destroy_no_free(if_lines);
558562
destroy_stack(stack);
563+
if (destroy_scope_on_exit) destroy_scope(scope);
559564
return return_value;
560565
}
561566
} else if (DEBUG) printf("Skipping if/elif block...\n");
@@ -599,6 +604,7 @@ void *execute(list *parsed_code, Scope *scope, W_Type return_type, bool destroy_
599604
if (DEBUG) printf("Else block executed !\n");
600605
if (return_value != NULL) {
601606
destroy_stack(stack);
607+
if (destroy_scope_on_exit) destroy_scope(scope);
602608
return return_value;
603609
} //!SECTION - else
604610
} else if (strcmp(word->value, "endif") == 0) {
@@ -648,6 +654,7 @@ void *execute(list *parsed_code, Scope *scope, W_Type return_type, bool destroy_
648654
continue;
649655
} else {
650656
destroy_stack(stack);
657+
if (destroy_scope_on_exit) destroy_scope(scope);
651658
return return_value;
652659
}
653660
}

src/lexer.c

+2-2
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,6 @@ list *word_tokenize(FILE *source) {
4141
int eval = 0; //if there is a word to eval
4242
int eval_str = 0; //if the word to eval is a litteral str
4343

44-
W_Word *w = (W_Word *)w_malloc(sizeof(W_Word));
4544
list *line = list_init();
4645
list_append(code, line);
4746
for (int i = 0; i < size; i++) {
@@ -62,13 +61,13 @@ list *word_tokenize(FILE *source) {
6261
char *value = (char *)w_malloc(i - start + 1);
6362
fread(value, 1, i - start, source);
6463
value[i - start] = '\0';
64+
W_Word *w = (W_Word *)w_malloc(sizeof(W_Word));
6565
w->value = value;
6666
w->type = word_type(value);
6767
w->line = n_line;
6868
w->parsed = false;
6969
w->is_generated = false;
7070
list_append(line, w);
71-
w = (W_Word *)w_malloc(sizeof(W_Word));
7271
eval = 0;
7372
}
7473
if (c == '\"') {
@@ -87,6 +86,7 @@ list *word_tokenize(FILE *source) {
8786
char *value = (char *)w_malloc(size - start + 1);
8887
fread(value, 1, size - start, source);
8988
value[size - start] = '\0';
89+
W_Word *w = (W_Word *)w_malloc(sizeof(W_Word));
9090
w->value = value;
9191
w->type = word_type(value);
9292
w->line = n_line;

src/parser.c

+19-4
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,13 @@ list *parse(list *tokenized_code) {
1515
list_append(parsed_code, current_block);
1616
current_line = current_line->next;
1717
}
18-
list_destroy(tokenized_code);
18+
current_line = tokenized_code->head;
19+
while (current_line != NULL) {
20+
list *line = (list *)current_line->value;
21+
list_destroy_no_free(line);
22+
current_line = current_line->next;
23+
}
24+
list_destroy_no_free(tokenized_code);
1925
return parsed_code;
2026
}
2127

@@ -167,13 +173,22 @@ void parser_destroy(list *parsed_code) {
167173
list_element *next_block = current_block->next;
168174
list *parsed_words = (list *)current_block->value;
169175
if (parsed_words != NULL) {
170-
list_destroy(parsed_words);
176+
list_element *current_word = parsed_words->head;
177+
while (current_word != NULL) {
178+
W_Word *word = (W_Word *)current_word->value;
179+
if (word != NULL) {
180+
w_free(word->value);
181+
w_free(word);
182+
}
183+
current_word = current_word->next;
184+
}
185+
list_destroy_no_free(parsed_words);
171186
}
172187
current_block = next_block;
173188
}
174-
list_destroy(line);
189+
list_destroy_no_free(line);
175190
}
176191
current_line = next_line;
177192
}
178-
list_destroy(parsed_code);
193+
list_destroy_no_free(parsed_code);
179194
}

src/w_alloc.c

+2-2
Original file line numberDiff line numberDiff line change
@@ -27,10 +27,10 @@ void *w_malloc(size_t size) {
2727
* \param ptr The memory to free.
2828
*/
2929
void w_free(void *ptr) {
30-
printf("[MEMORY] Freeing memory at %p...\n", ptr);
30+
printf("[MEMORY] Freeing memory at %p... ", ptr);
3131
free(ptr);
3232
w_alloc_count--;
33-
// printf("Freed\n");
33+
printf("Freed\n");
3434
}
3535

3636
/**

src/w_dict.c

+8
Original file line numberDiff line numberDiff line change
@@ -120,6 +120,14 @@ void w_dict_remove(W_Dict *d, char *key) {
120120
* \return The stringified dictionary.
121121
*/
122122
char *w_dict_stringify(W_Dict *d) {
123+
if (d->keys->size == 0) {
124+
char *str = (char *)w_malloc(3);
125+
str[0] = '{';
126+
str[1] = '}';
127+
str[2] = '\0';
128+
return str;
129+
}
130+
123131
int size = 0;
124132
list_element *current_key = d->keys->head;
125133
W_List_Element *current_value = d->values->head;

src/w_function.c

+1
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ W_Func *w_func_init() {
2222
*/
2323
void w_func_destroy(W_Func *f) {
2424
dict_destroy(f->args);
25+
list_destroy_no_free(f->parsed_code);
2526
w_free(f);
2627
}
2728

src/w_list.c

+8
Original file line numberDiff line numberDiff line change
@@ -200,6 +200,14 @@ void w_list_concat(W_List *l1, W_List *l2) {
200200
* \return The string representation of the list
201201
**/
202202
char *w_list_stringify(W_List *l) {
203+
if (l->size == 0) {
204+
char *str = (char *)w_malloc(3);
205+
str[0] = '[';
206+
str[1] = ']';
207+
str[2] = '\0';
208+
return str;
209+
}
210+
203211
int size = 0;
204212
W_List_Element *e = l->head;
205213

0 commit comments

Comments
 (0)