Skip to content

Commit 3b06da0

Browse files
committed
refactor: optimize memory in quickdecode
1 parent 4cdd4fc commit 3b06da0

File tree

1 file changed

+48
-65
lines changed

1 file changed

+48
-65
lines changed

apriltag.c

Lines changed: 48 additions & 65 deletions
Original file line numberDiff line numberDiff line change
@@ -117,18 +117,28 @@ static double graymodel_interpolate(struct graymodel *gm, double x, double y)
117117
return gm->C[0]*x + gm->C[1]*y + gm->C[2];
118118
}
119119

120-
struct quick_decode_entry
120+
// Uses pragma to ensure this occupies exactly 3 bytes (no padding).
121+
#pragma pack(push, 1)
122+
struct quick_decode_val
121123
{
122-
uint64_t rcode; // the queried code
123-
uint16_t id; // the tag ID (a small integer)
124-
uint8_t hamming; // how many errors corrected?
125-
uint8_t rotation; // number of rotations [0, 3]
124+
uint16_t id;
125+
uint8_t hamming;
126126
};
127+
#pragma pack(pop)
127128

128129
struct quick_decode
129130
{
130131
int nentries;
131-
struct quick_decode_entry *entries;
132+
uint64_t *keys;
133+
struct quick_decode_val *values;
134+
};
135+
136+
struct quick_decode_entry
137+
{
138+
uint64_t rcode;
139+
uint16_t id;
140+
uint8_t hamming;
141+
uint8_t rotation;
132142
};
133143

134144
/**
@@ -173,13 +183,13 @@ static void quick_decode_add(struct quick_decode *qd, uint64_t code, int id, int
173183
{
174184
uint32_t bucket = code % qd->nentries;
175185

176-
while (qd->entries[bucket].rcode != UINT64_MAX) {
186+
while (qd->keys[bucket] != UINT64_MAX) {
177187
bucket = (bucket + 1) % qd->nentries;
178188
}
179189

180-
qd->entries[bucket].rcode = code;
181-
qd->entries[bucket].id = id;
182-
qd->entries[bucket].hamming = hamming;
190+
qd->keys[bucket] = code;
191+
qd->values[bucket].id = (uint16_t)id;
192+
qd->values[bucket].hamming = (uint8_t)hamming;
183193
}
184194

185195
static void quick_decode_uninit(apriltag_family_t *fam)
@@ -188,7 +198,8 @@ static void quick_decode_uninit(apriltag_family_t *fam)
188198
return;
189199

190200
struct quick_decode *qd = (struct quick_decode*) fam->impl;
191-
free(qd->entries);
201+
free(qd->keys);
202+
free(qd->values);
192203
free(qd);
193204
fam->impl = NULL;
194205
}
@@ -198,9 +209,9 @@ static void quick_decode_init(apriltag_family_t *family, int maxhamming)
198209
assert(family->impl == NULL);
199210
assert(family->ncodes < 65536);
200211

201-
struct quick_decode *qd = calloc(1, sizeof(struct quick_decode));
212+
struct quick_decode *qd = (struct quick_decode*)calloc(1, sizeof(struct quick_decode));
213+
202214
int capacity = family->ncodes;
203-
204215
int nbits = family->nbits;
205216

206217
if (maxhamming >= 1)
@@ -214,81 +225,49 @@ static void quick_decode_init(apriltag_family_t *family, int maxhamming)
214225

215226
qd->nentries = capacity * 3;
216227

217-
// debug_print("capacity %d, size: %.0f kB\n",
218-
// capacity, qd->nentries * sizeof(struct quick_decode_entry) / 1024.0);
228+
qd->keys = (uint64_t*)malloc(qd->nentries * sizeof(uint64_t));
229+
qd->values = (struct quick_decode_val*)malloc(qd->nentries * sizeof(struct quick_decode_val));
219230

220-
qd->entries = calloc(qd->nentries, sizeof(struct quick_decode_entry));
221-
if (qd->entries == NULL) {
222-
debug_print("Failed to allocate hamming decode table\n");
223-
// errno already set to ENOMEM (Error No MEMory) by calloc() failure
231+
if (qd->keys == NULL || qd->values == NULL) {
232+
// debug_print("Failed to allocate hamming decode table\n");
233+
if (qd->keys) free(qd->keys);
234+
if (qd->values) free(qd->values);
235+
free(qd);
224236
return;
225237
}
226238

227-
for (int i = 0; i < qd->nentries; i++)
228-
qd->entries[i].rcode = UINT64_MAX;
239+
for (int i = 0; i < qd->nentries; i++) {
240+
qd->keys[i] = UINT64_MAX;
241+
}
229242

230243
errno = 0;
231244

232245
for (uint32_t i = 0; i < family->ncodes; i++) {
233246
uint64_t code = family->codes[i];
234247

235-
// add exact code (hamming = 0)
248+
// Hamming 0
236249
quick_decode_add(qd, code, i, 0);
237250

238251
if (maxhamming >= 1) {
239-
// add hamming 1
240252
for (int j = 0; j < nbits; j++)
241253
quick_decode_add(qd, code ^ (APRILTAG_U64_ONE << j), i, 1);
242254
}
243255

244256
if (maxhamming >= 2) {
245-
// add hamming 2
246257
for (int j = 0; j < nbits; j++)
247258
for (int k = 0; k < j; k++)
248259
quick_decode_add(qd, code ^ (APRILTAG_U64_ONE << j) ^ (APRILTAG_U64_ONE << k), i, 2);
249260
}
250261

251262
if (maxhamming >= 3) {
252-
// add hamming 3
253-
for (int j = 0; j < nbits; j++)
263+
for (int j = 0; j < nbits; j++)
254264
for (int k = 0; k < j; k++)
255265
for (int m = 0; m < k; m++)
256266
quick_decode_add(qd, code ^ (APRILTAG_U64_ONE << j) ^ (APRILTAG_U64_ONE << k) ^ (APRILTAG_U64_ONE << m), i, 3);
257267
}
258-
259-
if (maxhamming > 3) {
260-
debug_print("\"maxhamming\" beyond 3 not supported\n");
261-
// set errno to Error INvalid VALue
262-
errno = EINVAL;
263-
return;
264-
}
265268
}
266269

267270
family->impl = qd;
268-
269-
#if 0
270-
int longest_run = 0;
271-
int run = 0;
272-
int run_sum = 0;
273-
int run_count = 0;
274-
275-
// This accounting code doesn't check the last possible run that
276-
// occurs at the wrap-around. That's pretty insignificant.
277-
for (int i = 0; i < qd->nentries; i++) {
278-
if (qd->entries[i].rcode == UINT64_MAX) {
279-
if (run > 0) {
280-
run_sum += run;
281-
run_count ++;
282-
}
283-
run = 0;
284-
} else {
285-
run ++;
286-
longest_run = imax(longest_run, run);
287-
}
288-
}
289-
290-
printf("quick decode: longest run: %d, average run %.3f\n", longest_run, 1.0 * run_sum / run_count);
291-
#endif
292271
}
293272

294273
// returns an entry with hamming set to 255 if no decode was found.
@@ -297,18 +276,22 @@ static void quick_decode_codeword(apriltag_family_t *tf, uint64_t rcode,
297276
{
298277
struct quick_decode *qd = (struct quick_decode*) tf->impl;
299278

300-
// qd might be null if detector_add_family_bits() failed
279+
// qd might be null if init failed
301280
for (int ridx = 0; qd != NULL && ridx < 4; ridx++) {
302-
303-
for (int bucket = rcode % qd->nentries;
304-
qd->entries[bucket].rcode != UINT64_MAX;
305-
bucket = (bucket + 1) % qd->nentries) {
306-
307-
if (qd->entries[bucket].rcode == rcode) {
308-
*entry = qd->entries[bucket];
281+
282+
uint32_t bucket = rcode % qd->nentries;
283+
284+
while (qd->keys[bucket] != UINT64_MAX) {
285+
286+
if (qd->keys[bucket] == rcode) {
287+
entry->rcode = rcode;
288+
entry->id = qd->values[bucket].id;
289+
entry->hamming = qd->values[bucket].hamming;
309290
entry->rotation = ridx;
310291
return;
311292
}
293+
294+
bucket = (bucket + 1) % qd->nentries;
312295
}
313296

314297
rcode = rotate90(rcode, tf->nbits);

0 commit comments

Comments
 (0)