Skip to content

Commit

Permalink
add c3
Browse files Browse the repository at this point in the history
  • Loading branch information
hwchen committed Feb 13, 2025
1 parent 0cbbe5a commit 1d65b58
Show file tree
Hide file tree
Showing 5 changed files with 265 additions and 1 deletion.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ crystal/.shards/
crystal/crystal
ols.json
odin/related
c3/related
.dart_tool/
v/related

Expand Down
6 changes: 6 additions & 0 deletions Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,12 @@ RUN wget 'https://github.com/odin-lang/Odin/releases/download/dev-2023-12/odin-u

RUN unzip /home/builduser/odin.zip -d /home/builduser/odin

# install c3
RUN wget 'https://github.com/c3lang/c3c/releases/download/v0.6.6/c3-linux.tar.gz' -O /home/builduser/c3.tar.gz

RUN tar zxvf /home/builduser/c3.tar.gz -C /home/builduser
ENV PATH="$PATH:/home/builduser/c3"

# install vlang
RUN wget 'https://github.com/vlang/v/releases/download/weekly.2023.40.1/v_linux.zip' -O /home/builduser/v.zip

Expand Down
120 changes: 120 additions & 0 deletions c3/json.c3
Original file line number Diff line number Diff line change
@@ -0,0 +1,120 @@
// Using std::collections::object to serialize json would require
// converting into objects; instead, I just directly wrote the json.
// It's ugly copy-paste one-time-use code.
module related::json;

import std::collections;
import std::encoding::json;
import std::io;

// Nothing is freed, and assumes that input outlives output
fn Post[]! parse_posts(String input, Allocator alloc = allocator::heap()) {
List(<Post>) posts;
Object* posts_arr = json::parse_string(input, alloc)!;
foreach (i, post_obj : posts_arr.array) {
List(<String>) tags;
foreach (tag_obj : post_obj.get("tags")!.array) {
tags.push(tag_obj.s);
}
posts.push(Post {
._id = post_obj.get_string("_id")!,
.title = post_obj.get_string("title")!,
.tags = tags.array_view(),
});
}
return posts.array_view();
}

fn String Post.to_json(post) {
DString out;
out.append('{');

// _id
out.append("\"_id\":\"");
out.append(post._id);
out.append("\",");

// title
out.append("\"title\":\"");
out.append(post.title);
out.append("\",");

// tags
out.append("\"tags\":[");
if (post.tags.len > 0) {
out.append("\"");
out.append(post.tags[0]);
out.append("\"");
}
if (post.tags.len > 1) {
foreach(tag : post.tags[1..]) {
out.append(",");
out.append("\"");
out.append(tag);
out.append("\"");
}
}
out.append("]");
out.append('}');
return out.str_view();
}

fn String TopPosts.to_json(topposts) {
DString out;
out.append('{');

// _id
out.append("\"_id\":\"");
out.append(*topposts._id);
out.append("\",");

// tags
out.append("\"tags\":[");
if (topposts.tags.len > 0) {
out.append("\"");
out.append((*topposts.tags)[0]);
out.append("\"");
}
if (topposts.tags.len > 1) {
foreach(tag : (*topposts.tags)[1..]) {
out.append(",");
out.append("\"");
out.append(tag);
out.append("\"");
}
}
out.append("],");

// related
out.append("\"related\":[");
if (topposts.related.len > 0) {
out.append(topposts.related[0].to_json());
}
if (topposts.related.len > 1) {
foreach(related_post : topposts.related[1..]) {
out.append(',');
out.append(related_post.to_json());
}
}
out.append("]");

// finish
out.append('}');
return out.str_view();
}

fn String TopPosts[].to_json(topposts_list) {
DString out;
out.append("[\n");
if (topposts_list.len > 0) {
out.append(topposts_list[0].to_json());
}
if (topposts_list.len > 1) {
foreach(topposts : topposts_list[1..]) {
out.append(",\n");
out.append(topposts.to_json());
}
}
out.append("\n]");
return out.str_view();
}
122 changes: 122 additions & 0 deletions c3/related.c3
Original file line number Diff line number Diff line change
@@ -0,0 +1,122 @@
module related;

import std::collections;
import std::core::mem;
import std::io;
import std::time::clock;

struct Post {
String _id;
String title;
Tag[] tags;
}
def Posts = Post[];

def PostIdx = uint;
def PostIdxList = List(<PostIdx>);
def Tag = String;
def Tag2PostIdxList = HashMap(<Tag, PostIdxList>);

struct TopPosts {
String* _id;
Tag[]* tags;
Post*[] related;
}
struct Score {
char s;
uint pos;
}

fn char is_top(char m, char[] score) @inline {
char x;
foreach (s : score) {
// TODO is there a compiler builtin for bool_to_int?
x |= (s > m) ? 1 : 0;
}
return x;
}

fn void get_top(uint b, char[] score, char* min, Score[] t5) @inline {
uint i = b;
int score_idx;
while(score_idx < score.len) {
char s = score[score_idx];
if (s > *min) {
ichar u = 3;
while (u >= 0 && s > t5[u].s) {
t5[u + 1] = t5[u];
u -= 1;
}
t5[u + 1] = Score{ .s = s, .pos = i };
*min = t5[4].s;
}
i += 1;
score_idx += 1;
}
}

fn void top5(Post*[] related, char[] score, Post[] ps) @inline {
Score s = { .s = 0, .pos = 0 };
Score[5] t5 = { s, s, s, s, s };
char min_tags;

uint b;
uint cache_line = 64;
while (b < score.len) {
uint e = min(b + cache_line, (uint)score.len);
char[] chunk = score[b..e - 1];
if (is_top(min_tags, chunk) > 0) {
get_top(b, chunk, &min_tags, t5[0..]);
}
b += cache_line;
}
foreach (i, t : t5) {
related[i] = &ps[t.pos];
}
}

fn void main() {
String input = (String)file::load_new("../posts.json")!!;
Post[] posts = json::parse_posts(input)!!;

Clock start = clock::now();

Tag2PostIdxList tag2postidxs;
foreach (post_idx, post : posts) {
foreach (tag : post.tags) {
// TODO hashmap doesn't have the API
// that would remove extra hash comparisons
if (!tag2postidxs.has_key(tag)) {
tag2postidxs.set(tag, {});
}
tag2postidxs.get_ref(tag)!!.push((uint)post_idx);
}
}

TopPosts[] op = mem::new_array(TopPosts, posts.len);
Post*[] rl = mem::new_array(Post*, posts.len * 5);

char[] tagged_post_count = mem::new_array(char, posts.len);

for (int post_idx = 0; post_idx < posts.len; post_idx += 1) {
// reset tagged_post_count
mem::zero_volatile(tagged_post_count);

foreach (tag : posts[post_idx].tags) {
foreach (tagged_post_idx : tag2postidxs.get(tag)!!) {
tagged_post_count[tagged_post_idx] += 1;
}
}

tagged_post_count[post_idx] = 0; // Don't count self

Post*[] related = rl[post_idx * 5 : 5];
top5(related, tagged_post_count, posts);
op[post_idx] = { ._id = &posts[post_idx]._id, .tags = &posts[post_idx].tags, .related = related };
}
NanoDuration end = start.to_now();
io::printf("Processing time (w/o IO): %sms\n", end.to_ms());

File op_file = file::open("../related_posts_c3.json", "wb")!!;
io::fprint(&op_file, op.to_json())!!;
}
17 changes: 16 additions & 1 deletion run.sh
Original file line number Diff line number Diff line change
Expand Up @@ -253,6 +253,14 @@ run_odin() {
check_output "related_posts_odin.json"
}

run_c3() {
echo "Running c3" &&
cd ./c3 &&
c3c compile related.c3 json.c3 -O5 &&
run_command "c3" $runs ./related &&
check_output "related_posts_c3.json"
}

run_vlang() {
echo "Running Vlang" &&
cd ./v &&
Expand Down Expand Up @@ -763,6 +771,10 @@ elif [ "$first_arg" = "odin" ]; then

run_odin

elif [ "$first_arg" = "c3" ]; then

run_c3

elif [ "$first_arg" = "jq" ]; then

run_jq
Expand Down Expand Up @@ -972,6 +984,7 @@ elif [ "$first_arg" = "all" ]; then
run_julia_highly_optimized || echo -e "\n" &&
run_julia_con || echo -e "\n" &&
run_odin || echo -e "\n" &&
run_c3 || echo -e "\n" &&
run_vlang || echo -e "\n" &&
run_dart || echo -e "\n" &&
run_dart_aot || echo -e "\n" &&
Expand Down Expand Up @@ -1024,6 +1037,8 @@ elif [ "$first_arg" = "clean" ]; then
cd .. &&
cd rust_con && cargo clean &&
cd .. &&
cd c3 && rm -f related &&
cd .. &&
cd d && rm -f related &&
cd .. &&
cd d_con && rm -f related_concurrent &&
Expand All @@ -1050,6 +1065,6 @@ elif [ "$first_arg" = "clean" ]; then

else

echo "Valid args: go | go_con | rust | rust_con | d | d_con | r | py | numpy | erlang | cl | numba | numba_con | cr | zig | zig_con | odin | jq | julia | julia_highly_optimized | julia_con | v | dart | swift | swift_con | node | bun | deno | java | java_graal | java_graal_con | nim | luajit | lua | fsharp | fsharp_aot | fsharp_con | csharp | csharp_aot | dascript | all | clean. Unknown argument: $first_arg"
echo "Valid args: go | go_con | rust | rust_con | d | d_con | r | py | numpy | erlang | cl | numba | numba_con | cr | zig | zig_con | odin | c3 | jq | julia | julia_highly_optimized | julia_con | v | dart | swift | swift_con | node | bun | deno | java | java_graal | java_graal_con | nim | luajit | lua | fsharp | fsharp_aot | fsharp_con | csharp | csharp_aot | dascript | all | clean. Unknown argument: $first_arg"

fi

0 comments on commit 1d65b58

Please sign in to comment.