Skip to content

Commit b5a22d8

Browse files
committed
Add runtime profiler
Based on our observation, a high percentage of true hotspots involve loops or backward jumps, but the number of IR is unstable within these true hotspots. Therefore, we believe our profiler can use three indices to detect hotspots: 1. Backward jump 2. Loop 3. Used frequency Close: #189
1 parent 844e9fc commit b5a22d8

File tree

8 files changed

+125
-66
lines changed

8 files changed

+125
-66
lines changed

src/cache.c

Lines changed: 0 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -13,14 +13,6 @@
1313
#include "mpool.h"
1414
#include "utils.h"
1515

16-
/* Currently, THRESHOLD is set to identify hot spots. Once the using frequency
17-
* for a block exceeds the THRESHOLD, the tier-1 JIT compiler process is
18-
* triggered.
19-
* FIXME: Implement effective profiler to detect hot spots, instead of simply
20-
* relying on THRESHOLD.
21-
*/
22-
#define THRESHOLD 4096
23-
2416
static uint32_t cache_size, cache_size_bits;
2517
static struct mpool *cache_mp;
2618

src/cache.h

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,12 @@
88
#include <stdbool.h>
99
#include <stdint.h>
1010

11+
/* Currently, THRESHOLD is set to identify hot spots. Once the using frequency
12+
* for a block exceeds the THRESHOLD, the tier-1 JIT compiler process is
13+
* triggered.
14+
*/
15+
#define THRESHOLD 4096
16+
1117
struct cache;
1218

1319
/** cache_create - crate a new cache

src/emulate.c

Lines changed: 29 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -305,6 +305,7 @@ static block_t *block_alloc(riscv_t *rv)
305305
block->translatable = true;
306306
block->hot = false;
307307
block->backward = false;
308+
block->loop = false;
308309
INIT_LIST_HEAD(&block->list);
309310
#endif
310311
return block;
@@ -383,6 +384,11 @@ static bool is_branch_taken = false;
383384
/* record the program counter of the previous block */
384385
static uint32_t last_pc = 0;
385386

387+
#if RV32_HAS(JIT)
388+
static set_t pc_set;
389+
static bool loop = false;
390+
#endif
391+
386392
/* Interpreter-based execution path */
387393
#define RVOP(inst, code, asm) \
388394
static bool do_##inst(riscv_t *rv, rv_insn_t *ir, uint64_t cycle, \
@@ -1040,6 +1046,22 @@ static block_t *block_find_or_translate(riscv_t *rv)
10401046
}
10411047

10421048
#if RV32_HAS(JIT)
1049+
static bool runtime_profiler(riscv_t *rv, block_t *block)
1050+
{
1051+
/* Based on our observation, a high percentage of true hotspots involve high
1052+
* using frequency, loops or backward jumps. Therefore, we believe our
1053+
* profiler can use three indices to detect hotspots */
1054+
uint32_t freq = cache_freq(rv->block_cache, block->pc_start);
1055+
/* to profile the block after chaining, the block should be executed first
1056+
*/
1057+
if (freq >= 2 && (block->backward || block->loop))
1058+
return true;
1059+
/* using frequency exceeds predetermined threshold */
1060+
if (freq == THRESHOLD)
1061+
return true;
1062+
return false;
1063+
}
1064+
10431065
typedef void (*exec_block_func_t)(riscv_t *rv, uintptr_t);
10441066
#endif
10451067

@@ -1117,17 +1139,16 @@ void rv_step(riscv_t *rv, int32_t cycles)
11171139
prev = NULL;
11181140
continue;
11191141
} /* check if using frequency of block exceed threshold */
1120-
else if (block->translatable &&
1121-
((block->backward &&
1122-
cache_freq(rv->block_cache, block->pc_start) >= 1024) ||
1123-
cache_hot(rv->block_cache, block->pc_start))) {
1142+
else if (block->translatable && runtime_profiler(rv, block)) {
11241143
block->hot = true;
11251144
block->offset = jit_translate(rv, block);
11261145
((exec_block_func_t) state->buf)(
11271146
rv, (uintptr_t) (state->buf + block->offset));
11281147
prev = NULL;
11291148
continue;
11301149
}
1150+
set_reset(&pc_set);
1151+
loop = false;
11311152
#endif
11321153
/* execute the block by interpreter */
11331154
const rv_insn_t *ir = block->ir_head;
@@ -1136,6 +1157,10 @@ void rv_step(riscv_t *rv, int32_t cycles)
11361157
prev = NULL;
11371158
break;
11381159
}
1160+
#if RV32_HAS(JIT)
1161+
if (loop && !block->loop)
1162+
block->loop = true;
1163+
#endif
11391164
prev = block;
11401165
}
11411166
}

src/jit.c

Lines changed: 0 additions & 54 deletions
Original file line numberDiff line numberDiff line change
@@ -1163,60 +1163,6 @@ static void muldivmod(struct jit_state *state,
11631163
}
11641164
#endif /* RV32_HAS(EXT_M) */
11651165

1166-
#define SET_SIZE_BITS 10
1167-
#define SET_SIZE (1 << SET_SIZE_BITS)
1168-
#define SET_SLOTS_SIZE 32
1169-
HASH_FUNC_IMPL(set_hash, SET_SIZE_BITS, 1 << SET_SIZE_BITS);
1170-
1171-
/* The set consists of SET_SIZE buckets, with each bucket containing
1172-
* SET_SLOTS_SIZE slots.
1173-
*/
1174-
typedef struct {
1175-
uint32_t table[SET_SIZE][SET_SLOTS_SIZE];
1176-
} set_t;
1177-
1178-
/**
1179-
* set_reset - clear a set
1180-
* @set: a pointer points to target set
1181-
*/
1182-
static inline void set_reset(set_t *set)
1183-
{
1184-
memset(set, 0, sizeof(set_t));
1185-
}
1186-
1187-
/**
1188-
* set_add - insert a new element into the set
1189-
* @set: a pointer points to target set
1190-
* @key: the key of the inserted entry
1191-
*/
1192-
static bool set_add(set_t *set, uint32_t key)
1193-
{
1194-
const uint32_t index = set_hash(key);
1195-
uint8_t count = 0;
1196-
while (set->table[index][count]) {
1197-
if (set->table[index][count++] == key)
1198-
return false;
1199-
}
1200-
1201-
set->table[index][count] = key;
1202-
return true;
1203-
}
1204-
1205-
/**
1206-
* set_has - check whether the element exist in the set or not
1207-
* @set: a pointer points to target set
1208-
* @key: the key of the inserted entry
1209-
*/
1210-
static bool set_has(set_t *set, uint32_t key)
1211-
{
1212-
const uint32_t index = set_hash(key);
1213-
for (uint8_t count = 0; set->table[index][count]; count++) {
1214-
if (set->table[index][count] == key)
1215-
return true;
1216-
}
1217-
return false;
1218-
}
1219-
12201166
static void prepare_translate(struct jit_state *state)
12211167
{
12221168
#if defined(__x86_64__)

src/riscv_private.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,7 @@ typedef struct block {
6868
uint32_t offset;
6969
bool
7070
translatable; /**< Determine the block has RV32AF insturctions or not */
71+
bool loop; /**< Determine the block has loop or not */
7172
struct list_head list;
7273
#endif
7374
} block_t;

src/rv32_template.c

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -149,6 +149,8 @@ RVOP(
149149
if (taken) {
150150
#if RV32_HAS(JIT)
151151
cache_get(rv->block_cache, PC, true);
152+
if (!set_add(&pc_set, PC))
153+
loop = true;
152154
if (cache_hot(rv->block_cache, PC))
153155
goto end_insn;
154156
#endif
@@ -248,6 +250,8 @@ RVOP(
248250
IIF(RV32_HAS(JIT)) \
249251
({ \
250252
cache_get(rv->block_cache, PC + 4, true); \
253+
if (!set_add(&pc_set, PC + 4)) \
254+
loop = true; \
251255
if (cache_hot(rv->block_cache, PC + 4)) \
252256
goto nextop; \
253257
}, ); \
@@ -265,6 +269,8 @@ RVOP(
265269
IIF(RV32_HAS(JIT)) \
266270
({ \
267271
cache_get(rv->block_cache, PC, true); \
272+
if (!set_add(&pc_set, PC)) \
273+
loop = true; \
268274
if (cache_hot(rv->block_cache, PC)) \
269275
goto end_insn; \
270276
}, ); \
@@ -1850,6 +1856,8 @@ RVOP(
18501856
if (taken) {
18511857
#if RV32_HAS(JIT)
18521858
cache_get(rv->block_cache, PC, true);
1859+
if (!set_add(&pc_set, PC))
1860+
loop = true;
18531861
if (cache_hot(rv->block_cache, PC))
18541862
goto end_insn;
18551863
#endif
@@ -2011,6 +2019,8 @@ RVOP(
20112019
if (taken) {
20122020
#if RV32_HAS(JIT)
20132021
cache_get(rv->block_cache, PC, true);
2022+
if (!set_add(&pc_set, PC))
2023+
loop = true;
20142024
if (cache_hot(rv->block_cache, PC))
20152025
goto end_insn;
20162026
#endif
@@ -2044,6 +2054,8 @@ RVOP(
20442054
goto nextop;
20452055
#if RV32_HAS(JIT)
20462056
cache_get(rv->block_cache, PC + 2, true);
2057+
if (!set_add(&pc_set, PC + 2))
2058+
loop = true;
20472059
if (cache_hot(rv->block_cache, PC + 2))
20482060
goto nextop;
20492061
#endif
@@ -2057,6 +2069,8 @@ RVOP(
20572069
if (taken) {
20582070
#if RV32_HAS(JIT)
20592071
cache_get(rv->block_cache, PC, true);
2072+
if (!set_add(&pc_set, PC))
2073+
loop = true;
20602074
if (cache_hot(rv->block_cache, PC))
20612075
goto end_insn;
20622076
#endif
@@ -2099,6 +2113,8 @@ RVOP(
20992113
goto nextop;
21002114
#if RV32_HAS(JIT)
21012115
cache_get(rv->block_cache, PC + 2, true);
2116+
if (!set_add(&pc_set, PC + 2))
2117+
loop = true;
21022118
if (cache_hot(rv->block_cache, PC + 2))
21032119
goto nextop;
21042120
#endif
@@ -2112,6 +2128,8 @@ RVOP(
21122128
if (taken) {
21132129
#if RV32_HAS(JIT)
21142130
cache_get(rv->block_cache, PC, true);
2131+
if (!set_add(&pc_set, PC))
2132+
loop = true;
21152133
if (cache_hot(rv->block_cache, PC))
21162134
goto end_insn;
21172135
#endif

src/utils.c

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -164,3 +164,43 @@ char *sanitize_path(const char *input)
164164

165165
return ret;
166166
}
167+
168+
HASH_FUNC_IMPL(set_hash, SET_SIZE_BITS, 1 << SET_SIZE_BITS);
169+
170+
void set_reset(set_t *set)
171+
{
172+
memset(set, 0, sizeof(set_t));
173+
}
174+
175+
/**
176+
* set_add - insert a new element into the set
177+
* @set: a pointer points to target set
178+
* @key: the key of the inserted entry
179+
*/
180+
bool set_add(set_t *set, uint32_t key)
181+
{
182+
const uint32_t index = set_hash(key);
183+
uint8_t count = 0;
184+
while (set->table[index][count]) {
185+
if (set->table[index][count++] == key)
186+
return false;
187+
}
188+
189+
set->table[index][count] = key;
190+
return true;
191+
}
192+
193+
/**
194+
* set_has - check whether the element exist in the set or not
195+
* @set: a pointer points to target set
196+
* @key: the key of the inserted entry
197+
*/
198+
bool set_has(set_t *set, uint32_t key)
199+
{
200+
const uint32_t index = set_hash(key);
201+
for (uint8_t count = 0; set->table[index][count]; count++) {
202+
if (set->table[index][count] == key)
203+
return true;
204+
}
205+
return false;
206+
}

src/utils.h

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -118,3 +118,34 @@ static inline void list_del_init(struct list_head *node)
118118
&entry->member != (head); \
119119
entry = safe, safe = list_entry(safe->member.next, type, member))
120120
#endif
121+
122+
#define SET_SIZE_BITS 10
123+
#define SET_SIZE (1 << SET_SIZE_BITS)
124+
#define SET_SLOTS_SIZE 32
125+
126+
/* The set consists of SET_SIZE buckets, with each bucket containing
127+
* SET_SLOTS_SIZE slots.
128+
*/
129+
typedef struct {
130+
uint32_t table[SET_SIZE][SET_SLOTS_SIZE];
131+
} set_t;
132+
133+
/**
134+
* set_reset - clear a set
135+
* @set: a pointer points to target set
136+
*/
137+
void set_reset(set_t *set);
138+
139+
/**
140+
* set_add - insert a new element into the set
141+
* @set: a pointer points to target set
142+
* @key: the key of the inserted entry
143+
*/
144+
bool set_add(set_t *set, uint32_t key);
145+
146+
/**
147+
* set_has - check whether the element exist in the set or not
148+
* @set: a pointer points to target set
149+
* @key: the key of the inserted entry
150+
*/
151+
bool set_has(set_t *set, uint32_t key);

0 commit comments

Comments
 (0)