Skip to content

Commit 1be34e6

Browse files
committed
Implementation and draft integration of binary expression tree
This replaces the stack-based calculator with a binary expression tree. The tree is constructed only once per metric when a perfgroup is read and can be evaluated arbitrarily often.
1 parent 894ea4a commit 1be34e6

File tree

7 files changed

+403
-17
lines changed

7 files changed

+403
-17
lines changed

src/calculator_exptree.c

Lines changed: 271 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,271 @@
1+
#include <bstrlib.h>
2+
#include <bstrlib_helper.h>
3+
#include <perfgroup.h> /* CounterList */
4+
#include "calculator_exptree.h"
5+
6+
#include <ctype.h>
7+
#include <inttypes.h>
8+
#include <stdio.h>
9+
#include <stdlib.h>
10+
#include <string.h>
11+
12+
13+
struct exptree_node {
14+
struct exptree_node *left; // Left child
15+
struct exptree_node *right; // Right child
16+
17+
double value; // Operand value (if it's a number)
18+
char *counter_name;
19+
char operator; // Operator: '+', '-', '*', '/'
20+
};
21+
22+
// Forward declarations
23+
static struct exptree_node *_make_expression_tree(const char **expr);
24+
static struct exptree_node *_make_term_tree(const char **expr);
25+
static struct exptree_node *_make_factor_tree(const char **expr);
26+
27+
#define NODE_NULL_VALUE 0.0
28+
#define NODE_NULL_OPERATOR '\0'
29+
30+
static void _skip_spaces(const char **expr)
31+
{
32+
while (isspace(**expr)) {
33+
(*expr)++;
34+
}
35+
}
36+
37+
// Set value and create a leaf node
38+
static struct exptree_node *_make_value_node(double value)
39+
{
40+
struct exptree_node *node = malloc(sizeof(struct exptree_node));
41+
if (!node) {
42+
return NULL;
43+
}
44+
*node = (struct exptree_node){.left = NULL,
45+
.right = NULL,
46+
.value = value,
47+
.counter_name = NULL,
48+
.operator= NODE_NULL_OPERATOR };
49+
return node;
50+
}
51+
52+
// Set counter and create a leaf node
53+
static struct exptree_node *_make_counter_node(char *counter)
54+
{
55+
struct exptree_node *node =
56+
(struct exptree_node *)malloc(sizeof(struct exptree_node));
57+
if (!node) {
58+
return NULL;
59+
}
60+
*node = (struct exptree_node){.left = NULL,
61+
.right = NULL,
62+
.value = NODE_NULL_VALUE,
63+
.counter_name = counter,
64+
.operator= NODE_NULL_OPERATOR };
65+
return node;
66+
}
67+
68+
// Parse an operator and create an operator node
69+
static struct exptree_node *
70+
_make_operator_node(char operator, struct exptree_node *left, struct exptree_node *right)
71+
{
72+
struct exptree_node *node =
73+
(struct exptree_node *)malloc(sizeof(struct exptree_node));
74+
if (!node) {
75+
return NULL;
76+
}
77+
*node = (struct exptree_node){.left = left,
78+
.right = right,
79+
.value = NODE_NULL_VALUE,
80+
.counter_name = NULL,
81+
.operator= operator};
82+
return node;
83+
}
84+
85+
// Parse factors: numbers or subexpressions in parentheses
86+
static struct exptree_node *_make_factor_tree(const char **expr)
87+
{
88+
_skip_spaces(expr);
89+
if (**expr == '(') {
90+
(*expr)++; // Skip '('
91+
// Recursively parse the subexpression:
92+
struct exptree_node *subtree = _make_expression_tree(expr);
93+
_skip_spaces(expr);
94+
if (**expr == ')') {
95+
(*expr)++; // Skip ')'
96+
} else {
97+
fprintf(stderr, "Error: Mismatched parentheses\n");
98+
exit(EXIT_FAILURE);
99+
}
100+
return subtree;
101+
} else {
102+
char *endptr;
103+
double value = strtod(*expr, &endptr);
104+
if (*expr == endptr) {
105+
// no conversion performed
106+
char *counter_name;
107+
if (sscanf(*expr, " %m[^()+-*/ \n] %*s", &counter_name) == 1) {
108+
*expr += strlen(counter_name);
109+
return _make_counter_node(counter_name);
110+
} else {
111+
fprintf(stderr, "Error: Could not parse: %s\n", *expr);
112+
exit(EXIT_FAILURE);
113+
}
114+
}
115+
*expr = endptr;
116+
return _make_value_node(value);
117+
}
118+
}
119+
120+
// Parse terms: handles multiplication and division
121+
static struct exptree_node *_make_term_tree(const char **expr)
122+
{
123+
struct exptree_node *left = _make_factor_tree(expr);
124+
while (1) {
125+
_skip_spaces(expr);
126+
if (**expr == '*' || **expr == '/') {
127+
char operator= ** expr;
128+
(*expr)++;
129+
struct exptree_node *right = _make_factor_tree(expr);
130+
left = _make_operator_node(operator, left, right);
131+
} else {
132+
break;
133+
}
134+
}
135+
return left;
136+
}
137+
138+
// Parse expressions: handles addition and subtraction
139+
static struct exptree_node *_make_expression_tree(const char **expr)
140+
{
141+
struct exptree_node *left = _make_term_tree(expr);
142+
while (1) {
143+
_skip_spaces(expr);
144+
if (**expr == '+' || **expr == '-') {
145+
char operator= ** expr;
146+
(*expr)++;
147+
struct exptree_node *right = _make_term_tree(expr);
148+
left = _make_operator_node(operator, left, right);
149+
} else {
150+
break;
151+
}
152+
}
153+
return left;
154+
}
155+
156+
struct exptree_node *make_expression_tree(const char *expr)
157+
{
158+
return _make_expression_tree(&expr);
159+
}
160+
161+
// Print the expression tree in in-order traversal
162+
static void _print_expression_tree(const struct exptree_node *node)
163+
{
164+
if (!node) {
165+
return;
166+
}
167+
if (node->operator) {
168+
printf("(");
169+
}
170+
_print_expression_tree(node->left);
171+
if (node->operator) {
172+
printf(" %c ", node->operator);
173+
} else if (node->counter_name) {
174+
printf("%s", node->counter_name);
175+
} else {
176+
printf("%g", node->value);
177+
}
178+
_print_expression_tree(node->right);
179+
if (node->operator) {
180+
printf(")");
181+
}
182+
}
183+
184+
// Print the expression tree in in-order traversal
185+
void print_expression_tree(const struct exptree_node *node)
186+
{
187+
if (!node) {
188+
printf("Empty expression tree\n");
189+
return;
190+
}
191+
_print_expression_tree(node);
192+
printf("\n");
193+
}
194+
195+
// Free the memory used by the tree
196+
void free_expression_tree(struct exptree_node *node)
197+
{
198+
if (!node) {
199+
return;
200+
}
201+
free_expression_tree(node->left);
202+
free_expression_tree(node->right);
203+
free(node->counter_name);
204+
free(node);
205+
}
206+
207+
// Get node value
208+
static double _get_value(const struct exptree_node *node, const CounterList *clist)
209+
{
210+
if (!node->counter_name) {
211+
return node->value;
212+
}
213+
214+
size_t len = strlen(node->counter_name);
215+
216+
/* TODO: set counter index when making the counter node to avoid redundant search */
217+
/* only ok if order does not change */
218+
for (int ctr = 0; clist->counters; ++ctr) {
219+
const char *cname = bdata(clist->cnames->entry[ctr]);
220+
221+
if (len == strlen(cname) && !strncmp(node->counter_name, cname, len)) {
222+
const char *val_str = bdata(clist->cvalues->entry[ctr]);
223+
/* TODO: why are counter values stored as strings instead of unsigned long
224+
* long ? */
225+
double val = strtod(val_str, NULL);
226+
/* TODO error handling of strtod */
227+
return val;
228+
}
229+
}
230+
231+
fprintf(stderr, "Error: counter not found: %s\n", node->counter_name);
232+
return NODE_NULL_VALUE; // TODO: error handling
233+
}
234+
235+
// Evaluate the expression tree recursively
236+
double evaluate_expression_tree(const struct exptree_node *node, const CounterList *clist)
237+
{
238+
// TODO: maybe return NAN to indicate error ?
239+
// need to check for NULL in child node evaluation in this case
240+
if (!node) {
241+
return 0.0;
242+
}
243+
244+
// If it's a leaf node (number/counter), return its value
245+
if (node->operator== NODE_NULL_OPERATOR) {
246+
return _get_value(node, clist);
247+
}
248+
249+
// Recursively evaluate left and right subtrees
250+
double val_left = evaluate_expression_tree(node->left, clist);
251+
double val_right = evaluate_expression_tree(node->right, clist);
252+
253+
// Apply the operator
254+
switch (node->operator) {
255+
case '+':
256+
return val_left + val_right;
257+
case '-':
258+
return val_left - val_right;
259+
case '*':
260+
return val_left * val_right;
261+
case '/':
262+
if (val_right == 0.0) {
263+
fprintf(stderr, "Error: Division by zero\n");
264+
exit(EXIT_FAILURE);
265+
}
266+
return val_left / val_right;
267+
default:
268+
fprintf(stderr, "Error: Unknown operator '%c'\n", node->operator);
269+
exit(EXIT_FAILURE);
270+
}
271+
}

src/includes/calculator_exptree.h

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
// calculator_exptree.h
2+
3+
struct exptree_node; // fwd declaration
4+
5+
// cannot fwd declare CounterList because it was an anonymous struct (changed)
6+
// thus we named CounterList to avoid unnecessary cyclic inclusion dependency with:
7+
// #include "perfgroup.h"
8+
struct CounterList; // fwd declaration
9+
10+
// TODO: documentation of interfaces
11+
// TODO: do we want "print_expression_tree"?
12+
13+
extern struct exptree_node *make_expression_tree(const char *expr);
14+
15+
extern void free_expression_tree(struct exptree_node *root);
16+
17+
extern double evaluate_expression_tree(const struct exptree_node *node, const struct CounterList *clist);
18+
19+
extern void print_expression_tree(const struct exptree_node *root);

src/includes/likwid.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,8 @@
3737

3838
#include <bstrlib.h>
3939

40+
#include "calculator_exptree.h" // fwd declaration of struct exptree_node
41+
4042
#define DEBUGLEV_ONLY_ERROR 0
4143
#define DEBUGLEV_INFO 1
4244
#define DEBUGLEV_DETAIL 2
@@ -1286,6 +1288,7 @@ typedef struct {
12861288
int nmetrics; /*!< \brief Number of metrics */
12871289
char **metricnames; /*!< \brief Metric names */
12881290
char **metricformulas; /*!< \brief Metric formulas */
1291+
struct exptree_node **metrictrees; /*!< \brief Metric expression trees */
12891292
char *longinfo; /*!< \brief Descriptive text about the group or empty */
12901293
} GroupInfo;
12911294

src/includes/perfgroup.h

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@
3434
#include <bstrlib_helper.h>
3535

3636
#include <likwid.h>
37+
#include "calculator_exptree.h"
3738

3839
typedef enum {
3940
GROUP_NONE = 0,
@@ -54,7 +55,7 @@ static char* groupFileSectionNames[MAX_GROUP_FILE_SECTIONS] = {
5455
"LUA"
5556
};
5657

57-
typedef struct {
58+
typedef struct CounterList {
5859
int counters; /*!< \brief Number of entries in the list */
5960
struct bstrList* cnames; /*!< \brief List of counter names */
6061
struct bstrList* cvalues; /*!< \brief List of counter values */
@@ -79,8 +80,7 @@ extern int update_clist(CounterList* clist, char* counter, double result);
7980
extern void destroy_clist(CounterList* clist);
8081

8182
extern int calc_metric(char* formula, CounterList* clist, double *result);
82-
83-
83+
extern int calc_metric_new(const struct exptree_node* tree, const CounterList* clist, double *result);
8484

8585

8686
#endif /* PERFGROUP_H */

0 commit comments

Comments
 (0)