hash2

Full source code

C++

// -*- mode: c++ -*-
// $Id$
// http://www.bagley.org/~doug/shootout/

#include <stdio.h>
#include <iostream>
#include <ext/hash_map>
#include <cstring>

using namespace std;
using namespace __gnu_cxx;

struct eqstr {
    bool operator()(const char* s1, const char* s2) const {
        return strcmp(s1, s2) == 0;
    }
};

int
main(int argc, char *argv[]) {
#ifdef SMALL_PROBLEM_SIZE
#define LENGTH 200
#else
#define LENGTH 2000
#endif
    int n = ((argc == 2) ? atoi(argv[1]) : LENGTH);
    char buf[16];
    typedef __gnu_cxx::hash_map<const char*, int, __gnu_cxx::hash<const char*>, eqstr> HM;
    HM hash1, hash2;

    for (int i=0; i<10000; i++) {
        sprintf(buf, "foo_%d", i);
        hash1[strdup(buf)] = i;
    }
    for (int i=0; i<n; i++) {
        for (HM::iterator k = hash1.begin(); k != hash1.end(); ++k) {
            hash2[(*k).first] += hash1[(*k).first];
        }
    }
    cout << hash1["foo_1"] << " " << hash1["foo_9999"] << " "
         << hash2["foo_1"] << " " << hash2["foo_9999"] << endl;
}

Rust

// Adapted from https://github.com/llvm/llvm-test-suite and
// http://www.bagley.org/~doug/shootout/
use std::env;
use std::collections::HashMap;

#[cfg(feature = "small_problem_size")]
const LENGTH: i32 = 200;

#[cfg(not(feature = "small_problem_size"))]
const LENGTH: i32 = 2000;

fn main() {
    let mut args = env::args();
    let n = if args.len() == 2 {
        args.nth(1).unwrap().parse::<i32>().unwrap()
    } else {
        LENGTH
    };

    let mut hash1: HashMap<String, i32> = HashMap::new();
    let mut hash2: HashMap<String, i32> = HashMap::new();

    for i in 0..10000 {
        let s = format!("foo_{}", i);
        hash1.insert(s, i);
    }

    for _i in 0..n {
        for (key, value) in &hash1 {
            hash2.entry(key.to_string()).and_modify(|e| { *e += *value }).or_insert(*value);
        }
    }

    println!("{} {} {} {}", hash1["foo_1"], hash1["foo_9999"], hash2["foo_1"], hash2["foo_9999"]);
}

Porting notes

Most of the code structure is similar to hash.

Iterating hash map entries and conditionally mutating another hash map

C++

	for (HM::iterator k = hash1.begin(); k != hash1.end(); ++k) {
	    hash2[(*k).first] += hash1[(*k).first];
	}

Rust

#![allow(unused)]
fn main() {
        for (key, value) in &hash1 {
            hash2.entry(key.to_string()).and_modify(|e| { *e += *value }).or_insert(*value);
        }
}

Similar to hash, because C++ hash_map's [] operator inserts a new entry if it's used with a new key while Rust HashMap's [] operator will panic if the key does not already exist in the hash map, we cannot just use Rust HashMap's [] operator.

Instead we use the and_modify and the or_insert methods to achieve similar behaviors. and_modify takes a closure |e| { *e += *value }, which will be executed when the key already exists in the hash map, or_insert takes a value for a new entry to be inserted when the key doesn't exist.

We need to convert the key of type &String to String when passing it to HashMap.entry because it takes the key type String, not a reference to it. Due to the cloning that is not necessary in the C++ version, this could be a potential performance disadvantage, but it doesn't seem to be the case according to the performance results where the Rust version was not slower. I suspect that the Rust compiler can optimize away the cloning.