Why is this golang code 3x faster than rust equivalent?
Github: https://github.com/jinyus/related\_post\_gen
The code generates related posts for a list of posts, sorted by the number of shared tags.
I tried my best to optimize the rust code but I have hit a wall. There's no cloning and the go version even has an extra struct where I used a tuple for rust. I'm just wondering how go could possibly be faster in this scenario. Code is below:
Rust Code
\t// truncated for brevity
\tlet mut post_tags_map: HashMap<&String, Vec<&Post>> = HashMap::new();
\tfor post in &posts {
\t\tfor tag in &post.tags {
\t\t\tpost_tags_map.entry(tag).or_default().push(post);
\t\t}
\t}
\tlet mut related_posts: Vec<RelatedPosts> = Vec::with_capacity(posts.len());
\tfor post in posts.iter() {
\t\tlet mut related_posts_map: HashMap<&Post, i8> = HashMap::new();
\t\tfor tag in &post.tags {
\t\t\tif let Some(tag_posts) = post_tags_map.get(tag) {
\t\t\t\tfor other_post in tag_posts {
\t\t\t\t\tif post._id != other_post._id {
\t\t\t\t\t\t*related_posts_map.entry(other_post).or_default() += 1;
\t\t\t\t\t}
\t\t\t\t}
\t\t\t}
\t\t}
\t\tlet mut related_posts_for_post: Vec<_> = related_posts_map.into_iter().collect();
\t\trelated_posts_for_post.sort_by(|post_a, post_b| post_b.1.cmp(&post_a.1));
\t\trelated_posts_for_post.truncate(5);
\t\trelated_posts.push(RelatedPosts {
\t\t\t_id: &post._id,
\t\t\ttags: &post.tags,
\t\t\trelated: related_posts_for_post
\t\t\t\t.into_iter()
\t\t\t\t.map(|(post, _)| post)
\t\t\t\t.collect(),
\t\t});
\t}
\t\t// truncated for brevity
Go Code
// truncated for brevity
\ttagMap := make(map[string][]*Post)
\tfor i := range posts {
\t\tfor _, tag := range posts[i].Tags {
\t\t\ttagMap[tag] = append(tagMap[tag], &posts[i])
\t\t}
\t}
\tallRelatedPosts := make([]RelatedPosts, len(posts))
\tfor i, post := range posts {
\t\trelatedPosts := make(map[*Post]int)
\t\tfor _, tag := range post.Tags {
\t\t\tfor _, relatedPost := range tagMap[tag] {
\t\t\t\tif relatedPost.ID != post.ID {
\t\t\t\t\trelatedPosts[relatedPost]++
\t\t\t\t}
\t\t\t}
\t\t}
\t\trelatedPostsSlice := make([]PostWithSharedTags, 0, len(relatedPosts))
\t\tfor v, count := range relatedPosts {
\t\t\trelatedPostsSlice = append(relatedPostsSlice, PostWithSharedTags{Post: v, SharedTags: count})
\t\t}
\t\tsort.Slice(relatedPostsSlice, func(i, j int) bool {
\t\t\treturn relatedPostsSlice[i].SharedTags > relatedPostsSlice[j].SharedTags
\t\t})
\t\tnum := min(5, len(relatedPostsSlice))
\t\ttopPosts := make([]*Post, num)
\t\tfor i := 0; i < num; i++ {
\t\t\ttopPosts[i] = relatedPostsSlice[i].Post
\t\t}
\t\tallRelatedPosts[i] = RelatedPosts{
\t\t\tID: post.ID,
\t\t\tTags: post.Tags,
\t\t\tRelated: topPosts,
\t\t}
\t}
// truncated for brevity