Skip to content

Commit bf5f0e5

Browse files
committed
Use array instead of linked list for PCR buckets
1 parent 6a2748a commit bf5f0e5

11 files changed

+61
-53
lines changed

src/main/resources/runtime/runtime.h

+11-49
Original file line numberDiff line numberDiff line change
@@ -22,13 +22,6 @@ struct PCR
2222
struct PCR *next;
2323
};
2424

25-
struct PCRBucket
26-
{
27-
int scc;
28-
struct PCR *pcrs;
29-
struct PCRBucket *next;
30-
};
31-
3225
// Common object header
3326
typedef struct
3427
{
@@ -45,7 +38,8 @@ struct FreeCell
4538
void (*free)(void *);
4639
};
4740

48-
struct PCRBucket *pcrBuckets = NULL;
41+
struct PCR **pcrBuckets = NULL;
42+
int numSccs = 0;
4943
struct FreeCell *freeList = NULL;
5044

5145
/** For dropping objects that never get assigned anywhere */
@@ -65,32 +59,15 @@ void addPCR(
6559
return;
6660
obj->addedPCR = 1;
6761

68-
struct PCRBucket **prev = &pcrBuckets;
69-
while (*prev != NULL && (*prev)->scc < scc)
70-
{
71-
prev = &(*prev)->next;
72-
}
73-
7462
struct PCR *pcr = malloc(sizeof(struct PCR));
7563
pcr->obj = obj;
7664
pcr->markGray = markGray;
7765
pcr->scan = scan;
7866
pcr->collectWhite = collectWhite;
7967
pcr->next = NULL;
8068

81-
if (*prev == NULL || scc < (*prev)->scc)
82-
{
83-
struct PCRBucket *newBucket = malloc(sizeof(struct PCRBucket));
84-
newBucket->scc = scc;
85-
newBucket->pcrs = pcr;
86-
newBucket->next = *prev;
87-
*prev = newBucket;
88-
}
89-
else
90-
{
91-
pcr->next = (*prev)->pcrs;
92-
(*prev)->pcrs = pcr;
93-
}
69+
pcr->next = pcrBuckets[scc];
70+
pcrBuckets[scc] = pcr;
9471
}
9572

9673
void removePCR(Common *obj, int scc)
@@ -99,26 +76,13 @@ void removePCR(Common *obj, int scc)
9976
return;
10077
obj->addedPCR = 0;
10178

102-
struct PCRBucket **prevBucket = &pcrBuckets;
103-
struct PCRBucket *bucket = pcrBuckets;
104-
while (bucket->scc != scc)
105-
{
106-
prevBucket = &bucket->next;
107-
bucket = bucket->next;
108-
}
109-
110-
struct PCR **prev = &bucket->pcrs;
111-
struct PCR *head = bucket->pcrs;
79+
struct PCR **prev = &pcrBuckets[scc];
80+
struct PCR *head = pcrBuckets[scc];
11281
while (head != NULL)
11382
{
11483
if (head->obj == obj)
11584
{
11685
*prev = head->next;
117-
if (bucket->pcrs == NULL) {
118-
// This was the only PCR in the bucket, so remove the bucket too
119-
*prevBucket = bucket->next;
120-
free(bucket);
121-
}
12286
free(head);
12387
return;
12488
}
@@ -171,20 +135,18 @@ void collectFreeList()
171135

172136
void processAllPCRs()
173137
{
174-
while (pcrBuckets != NULL)
138+
for (int scc = 0; scc < numSccs; scc ++)
175139
{
176-
markGrayAllPCRs(pcrBuckets->pcrs);
177-
scanAllPCRs(pcrBuckets->pcrs);
140+
markGrayAllPCRs(pcrBuckets[scc]);
141+
scanAllPCRs(pcrBuckets[scc]);
178142
if (freeList != NULL)
179143
{
180144
fprintf(stderr, "Free list should be null\n");
181145
exit(1);
182146
}
183-
collectWhiteAllPCRs(pcrBuckets->pcrs);
147+
collectWhiteAllPCRs(pcrBuckets[scc]);
148+
pcrBuckets[scc] = NULL;
184149
collectFreeList();
185-
struct PCRBucket *next = pcrBuckets->next;
186-
free(pcrBuckets);
187-
pcrBuckets = next;
188150
}
189151
}
190152

src/main/scala/fred/Translator.scala

+14-3
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ object Translator {
3131
)
3232
case RcAlgo.Mine => Cycles.fromFile(file)
3333
}
34-
val helper = Helper(typer)
34+
val helper = Helper(typer, cycles)
3535
val (genDecls, genImpls) =
3636
List(Freer, Decrementer, MarkGray, Scan, ScanBlack, CollectWhite, Printer)
3737
.flatMap(gen => file.typeDefs.map(td => (gen.decl(td), gen.impl(td))))
@@ -378,7 +378,7 @@ object Translator {
378378
*/
379379
private def tagName(ctorName: String): String = s"${ctorName}_tag"
380380

381-
private class Helper(typer: Typer) {
381+
private class Helper(typer: Typer, cycles: Cycles) {
382382

383383
/** Contains a mapping of mangled field names for every type */
384384
val mangledFieldsFor = mutable.Map.empty[TypeDef, Map[String, String]]
@@ -454,11 +454,22 @@ object Translator {
454454

455455
val signature = s"$typeToC ${mangleFnName(fn.name.value)}($params)"
456456

457+
val numSccs = cycles.sccs.size
458+
val allocPcrBuckets =
459+
if (fn.name.value == "main")
460+
indent(1)(s"""|pcrBuckets = calloc(sizeof(void *), $numSccs);
461+
|numSccs = $numSccs;""".stripMargin)
462+
else ""
463+
457464
val triggerGC =
458-
if (fn.name.value == "main") indent(1)("processAllPCRs();") else ""
465+
if (fn.name.value == "main")
466+
indent(1)("""|processAllPCRs();
467+
|free(pcrBuckets);""".stripMargin)
468+
else ""
459469

460470
val decl = s"$signature;"
461471
val impl = s"""|$signature {
472+
|$allocPcrBuckets
462473
|${indent(1)(paramsSetup)}
463474
|${indent(1)(bodySetup)}
464475
| $typeToC $resVar = $body;

src/test/resources/snapshot/exec/basic-cycle.c

+3
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/test/resources/snapshot/exec/basic-main.c

+3
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/test/resources/snapshot/exec/bucket-empty-recreate-3jilws.c

+3
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/test/resources/snapshot/exec/contrived-needs-sorting.c

+3
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/test/resources/snapshot/exec/immediate-drop.c

+3
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/test/resources/snapshot/exec/lazy-mark-scan-83wesh.c

+3
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/test/resources/snapshot/gen/complex-liudr567.c

+3
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

writeup/writeup.pdf

3.54 KB
Binary file not shown.

writeup/writeup.typ

+15-1
Original file line numberDiff line numberDiff line change
@@ -215,12 +215,26 @@ All of the stuff described above is then run 50,000 times. Here are the results:
215215
[Yes], [11054113602], [4.233204]
216216
)
217217

218-
Again, all this tells you is that there are some cases where my algorithm can do worse than lazy mark scan.
218+
After using an array like I mentioned above, I ran the stupid benchmark again. Now, both algorithms have about the same performance!
219+
#table(
220+
columns: (auto, auto, auto),
221+
table.header([Lazy mark scan only?], [Timestamp counter], [Clock (s)]),
222+
[No], [8890733476], [3.404776],
223+
[Yes], [10184751983], [3.900381]
224+
)
219225

220226
= Conclusion
221227

222228
= Future work <future_work>
223229

230+
== Formal verification
231+
232+
I worry that this algorithm isn't actually sound. It would be nice to prove using Coq or something that, if you group and sort PCRs according to the SCC of their type and process each group separately in order, you'll still collect all cycles.
233+
234+
== Applying this to newer algorithms
235+
236+
The papers I was working off are pretty old. I'd like
237+
224238
= Why name it #smallcaps[Fred]?
225239

226240
I was going to name it Foo, but there's already an esolang by that name that's fairly well-known (by esolang standards). So I went to the Wikipedia page on metasyntactic variables and picked "fred." I figured that if I needed to, I could pretend that it was something meaningful, like maybe an acronym or the name of a beloved childhood pet.

0 commit comments

Comments
 (0)