From ff31ca3f7512d0996ab8b65a9cbcf7d1554eb539 Mon Sep 17 00:00:00 2001 From: Alexander Slesarev Date: Tue, 11 Oct 2022 15:20:48 -0600 Subject: [PATCH] Made primes test more consistent by using unordered hashmaps and explicit sorts. (#426) --- README.md | 50 ++++++++++++++++++++++++------------------- primes/Primes.java | 2 ++ primes/primes.cpp | 6 +++--- primes/primes.cr | 1 + primes/primes.jl | 3 ++- primes/primes.js | 13 +++++------ primes/primes.lua | 2 +- primes/primes.nim | 6 ++++-- primes/primes.py | 2 ++ primes/primes.rb | 5 ++--- primes/primes.rs | 10 ++++----- primes/primes.scala | 1 + primes/primes.v | 1 + primes/primes.zig | 1 + primes/primes_jit.lua | 2 +- 15 files changed, 61 insertions(+), 44 deletions(-) diff --git a/README.md b/README.md index d55e80e1..39252ffa 100644 --- a/README.md +++ b/README.md @@ -362,32 +362,38 @@ Testing: - generating primes using the optimized [sieve of Atkin](https://www.geeksforgeeks.org/sieve-of-atkin/); - prefix search for their decimal numbers using Trie data structure. +Notes: + + - All languages but V and Python use unordered hashmaps (V and Python don't provide those out of box, and + their hashmaps use keys in the insertion order); + - The results are always sorted (could be unstable or stable though). + [Primes](primes) | Language | Time, s | Memory, MiB | Energy, J | | :----------------------- | ---------------------: | ------------------------------------------------: | ----------------------: | -| Zig | 0.059±0.000 | 0.92±00.02 + 49.27±00.13 | 2.41±00.02 | -| Rust | 0.094±0.000 | 0.93±00.01 + 78.38±00.06 | 3.79±00.02 | -| Crystal | 0.122±0.000 | 2.99±00.05 + 90.76±00.15 | 5.52±00.01 | -| C++/g++ | 0.132±0.000 | 3.57±00.05 + 85.38±00.39 | 5.38±00.06 | -| C++/clang++ | 0.140±0.000 | 1.67±00.01 + 76.83±00.03 | 5.49±00.04 | -| V/clang | 0.147±0.000 | 0.90±00.02 + 265.66±00.38 | 6.08±00.02 | -| V/gcc | 0.150±0.000 | 0.89±00.01 + 259.63±00.45 | 6.24±00.07 | -| Java | 0.157±0.002 | 37.73±00.17 + 153.30±04.68 | 8.82±00.11 | -| Node.js | 0.251±0.001 | 40.02±00.08 + 176.44±00.42 | 12.63±00.08 | -| Lua/luajit | 0.338±0.002 | 2.59±00.05 + 156.86±01.28 | 13.24±00.08 | -| Scala | 0.360±0.004 | 67.32±00.11 + 243.01±06.35 | 18.56±00.08 | -| Nim/clang | 0.423±0.001 | 1.94±00.01 + 1163.25±02.84 | 16.58±00.10 | -| Nim/gcc | 0.432±0.000 | 1.67±00.06 + 1170.86±01.80 | 16.62±00.14 | -| Julia | 0.604±0.001 | 245.48±00.27 + 376.33±00.44 | 23.40±00.14 | -| Python/pypy | 0.882±0.001 | 63.60±00.05 + 250.82±00.53 | 34.48±00.19 | -| Ruby/truffleruby (--jvm) | 1.396±0.036 | 348.32±03.87 + 486.92±22.89 | 91.61±02.10 | -| Ruby (--jit) | 1.445±0.003 | 270.51±00.03 + 147.09±00.07 | 58.62±00.50 | -| Lua | 1.483±0.005 | 2.27±00.03 + 283.81±00.55 | 57.70±00.54 | -| Ruby/truffleruby | 1.513±0.014 | 297.33±00.99 + 420.61±05.97 | 84.26±00.90 | -| Ruby/jruby | 1.991±0.045 | 183.52±01.96 + 523.52±28.85 | 103.87±03.74 | -| Ruby | 2.057±0.001 | 13.89±00.04 + 147.04±00.02 | 82.72±00.75 | -| Python | 4.979±0.045 | 10.53±00.05 + 234.86±00.90 | 193.43±02.10 | +| Zig | 0.059±0.000 | 0.92±00.01 + 48.53±00.03 | 2.45±00.02 | +| Crystal | 0.122±0.000 | 2.98±00.03 + 90.71±00.17 | 5.52±00.08 | +| Rust | 0.140±0.000 | 0.94±00.01 + 74.26±00.07 | 5.35±00.05 | +| Java | 0.159±0.002 | 38.27±00.10 + 153.79±03.78 | 8.89±00.06 | +| C++/g++ | 0.189±0.000 | 2.70±00.90 + 116.66±00.81 | 7.76±00.05 | +| C++/clang++ | 0.198±0.000 | 1.66±00.02 + 87.96±00.02 | 7.79±00.09 | +| V/clang | 0.212±0.001 | 1.91±00.05 + 203.15±01.44 | 8.46±00.06 | +| Node.js | 0.227±0.002 | 39.10±00.03 + 150.29±00.26 | 11.37±00.12 | +| V/gcc | 0.240±0.001 | 2.28±00.14 + 219.14±00.67 | 9.66±00.12 | +| Nim/clang | 0.297±0.001 | 2.07±00.01 + 601.73±02.96 | 11.72±00.09 | +| Lua/luajit | 0.338±0.002 | 1.20±00.02 + 157.12±01.04 | 13.22±00.05 | +| Nim/gcc | 0.352±0.003 | 1.68±00.07 + 614.62±00.26 | 13.48±00.20 | +| Scala | 0.362±0.006 | 67.43±00.09 + 248.88±11.36 | 18.63±00.35 | +| Julia | 0.597±0.001 | 246.42±00.24 + 374.41±01.06 | 23.23±00.15 | +| Python/pypy | 0.880±0.002 | 63.32±00.09 + 250.27±00.06 | 34.53±00.42 | +| Ruby/truffleruby (--jvm) | 1.381±0.026 | 354.60±10.97 + 552.99±34.23 | 90.57±01.36 | +| Ruby (--jit) | 1.442±0.004 | 270.43±00.06 + 147.02±00.05 | 59.14±00.99 | +| Lua | 1.478±0.004 | 2.28±00.01 + 283.04±00.48 | 57.46±00.33 | +| Ruby/truffleruby | 1.506±0.007 | 294.80±00.82 + 410.90±26.05 | 84.20±00.88 | +| Ruby/jruby | 1.990±0.057 | 182.28±01.67 + 538.37±28.70 | 102.14±03.48 | +| Ruby | 2.057±0.005 | 13.87±00.07 + 147.07±00.04 | 82.80±00.47 | +| Python | 5.022±0.024 | 10.49±00.04 + 234.72±00.26 | 196.62±02.52 | # Tests Execution diff --git a/primes/Primes.java b/primes/Primes.java index f3d51f3f..920307cb 100644 --- a/primes/Primes.java +++ b/primes/Primes.java @@ -2,6 +2,7 @@ import java.util.ArrayList; import java.util.Arrays; import java.util.BitSet; +import java.util.Collections; import java.util.HashMap; import java.util.LinkedList; import java.util.List; @@ -131,6 +132,7 @@ private static Iterable find(int upperBound, int prefix) { )); } } + Collections.sort(result); return result; } diff --git a/primes/primes.cpp b/primes/primes.cpp index c9d1f6e2..9346b5f9 100644 --- a/primes/primes.cpp +++ b/primes/primes.cpp @@ -1,9 +1,9 @@ #include #include -#include #include #include #include +#include #include #include @@ -18,7 +18,7 @@ static const auto UPPER_BOUND = 5'000'000; static const auto PREFIX = 32'338; struct Node { - std::map> children{}; + std::unordered_map> children{}; bool terminal = false; }; @@ -127,6 +127,7 @@ std::vector find(int upper_bound, int prefix) { queue.push(std::make_pair(v, prefix + ch)); } } + std::sort(result.begin(), result.end()); return result; } @@ -148,7 +149,6 @@ std::string to_string(const std::vector& a) { void verify() { std::vector left({2, 23, 29}); auto right = find(100, 2); - std::sort(right.begin(), right.end()); if (left != right) { std::cerr << to_string(left) << " != " << to_string(right) << std::endl; exit(EXIT_FAILURE); diff --git a/primes/primes.cr b/primes/primes.cr index 78ea3b0f..93bc249c 100644 --- a/primes/primes.cr +++ b/primes/primes.cr @@ -112,6 +112,7 @@ def find(upper_bound, prefix) queue.insert(0, {v, prefix + ch}) end end + result.sort! result end diff --git a/primes/primes.jl b/primes/primes.jl index 7196d141..92ce9cbb 100644 --- a/primes/primes.jl +++ b/primes/primes.jl @@ -142,7 +142,8 @@ function find(upper_bound::Int64, search_prefix::Int64)::Vector end end - return sort(result) + sort!(result) + return result end diff --git a/primes/primes.js b/primes/primes.js index 2e166f0d..134b5b9f 100644 --- a/primes/primes.js +++ b/primes/primes.js @@ -7,7 +7,7 @@ const PREFIX = 32338; class Node { constructor() { - this.children = new Map(); + this.children = new Object(); this.terminal = false; } } @@ -89,10 +89,10 @@ function generateTrie(l) { for (const el of l) { let head = root; for (const ch of el.toString()) { - if (!head.children.has(ch)) { - head.children.set(ch, new Node()); + if (!Object.hasOwn(head.children, ch)) { + head.children[ch] = new Node(); } - head = head.children.get(ch); + head = head.children[ch]; } head.terminal = true; } @@ -105,7 +105,7 @@ function find(upperBound, prefix) { let head = generateTrie(primes.toList()); for (const ch of strPrefix) { - head = head.children.get(ch); + head = head.children[ch]; if (typeof head === 'undefined') { return null; } @@ -117,10 +117,11 @@ function find(upperBound, prefix) { if (top.terminal) { result.push(toInt(prefix)); } - for (const [ch, v] of top.children) { + for (const [ch, v] of Object.entries(top.children)) { queue.splice(0, 0, [v, prefix + ch]); } } + result.sort(); return result; } diff --git a/primes/primes.lua b/primes/primes.lua index e2918ad2..57ba083e 100644 --- a/primes/primes.lua +++ b/primes/primes.lua @@ -146,7 +146,6 @@ local function generate_trie(l) end local function dump(arr) - table.sort(arr) return "[" .. table.concat(arr, ", ") .. "]" end @@ -173,6 +172,7 @@ local function find(upper_bound, prefix) queue.push({v, prefix .. ch}) end end + table.sort(result) return result end diff --git a/primes/primes.nim b/primes/primes.nim index 589ecae4..0f463c65 100644 --- a/primes/primes.nim +++ b/primes/primes.nim @@ -1,17 +1,17 @@ import deques import net import posix +import std/algorithm import strformat, strutils, sequtils import tables - const UPPER_BOUND: int = 5_000_000 const PREFIX: int = 32_338 type NodeRef = ref Node Node = object - children: OrderedTable[char, NodeRef] + children: Table[char, NodeRef] terminal: bool Sieve = object limit: int @@ -102,6 +102,8 @@ proc find(upperBound: int, prefix: int): seq[int] = for (ch, node) in pair.node.children.pairs(): queue.addFirst(Pair(node: node, str: pair.str & $ch)) + result.sort() + proc notify(msg: string) = let sock = net.dial("127.0.0.1", Port(9001)) defer: sock.close() diff --git a/primes/primes.py b/primes/primes.py index a126fa7f..76b52b6b 100644 --- a/primes/primes.py +++ b/primes/primes.py @@ -98,6 +98,8 @@ def find(upper_bound, prefix): result.append(int(prefix)) for ch, v in top.children.items(): queue.insert(0, (v, prefix + ch)) + + result.sort() return result diff --git a/primes/primes.rb b/primes/primes.rb index 8df6451d..6ca12806 100644 --- a/primes/primes.rb +++ b/primes/primes.rb @@ -110,10 +110,9 @@ def find(upper_bound, prefix) until queue.empty? (top, prefix) = queue.pop result.push(prefix.to_i) if top.terminal - top.children.each do |ch, v| - queue.insert(0, [v, prefix + ch]) - end + top.children.each { |ch, v| queue.insert(0, [v, prefix + ch]) } end + result.sort! result end diff --git a/primes/primes.rs b/primes/primes.rs index 65f3587b..f9a57196 100644 --- a/primes/primes.rs +++ b/primes/primes.rs @@ -1,4 +1,4 @@ -use std::collections::{BTreeMap, VecDeque}; +use std::collections::{HashMap, VecDeque}; use std::io::Write; use std::net::TcpStream; use std::process; @@ -8,14 +8,14 @@ const PREFIX: i32 = 32_338; #[derive(Debug)] struct Node { - children: BTreeMap>, + children: HashMap>, terminal: bool, } impl Node { fn new() -> Node { Node { - children: BTreeMap::new(), + children: HashMap::new(), terminal: false, } } @@ -142,6 +142,7 @@ fn find(upper_bound: usize, prefix: i32) -> Vec { queue.push_back((v, new_prefix)); } } + result.sort_unstable(); result } @@ -153,8 +154,7 @@ fn notify(msg: &str) { fn verify() { let left = vec![2, 23, 29]; - let mut right = find(100, 2); - right.sort_unstable(); + let right = find(100, 2); if left != right { eprintln!("{:?} != {:?}", left, right); process::exit(-1); diff --git a/primes/primes.scala b/primes/primes.scala index 3afe2629..a7ebfeab 100644 --- a/primes/primes.scala +++ b/primes/primes.scala @@ -114,6 +114,7 @@ object Primes { queue += ((v, prefix + ch)) } } + scala.util.Sorting.stableSort(result) Some(result) } case None => None diff --git a/primes/primes.v b/primes/primes.v index ede2ab86..fd646fff 100644 --- a/primes/primes.v +++ b/primes/primes.v @@ -117,6 +117,7 @@ fn find(upper_bound usize, prefix int) []int { queue.insert(0, Pair{v, pair.p_prefix + ch.ascii_str()}) } } + result.sort() return result } diff --git a/primes/primes.zig b/primes/primes.zig index 24b8dbc3..89ce9dde 100644 --- a/primes/primes.zig +++ b/primes/primes.zig @@ -153,6 +153,7 @@ fn find(alloc: std.mem.Allocator, upper_bound: i32, prefix: i32) std.ArrayList(i queue.prepend(elem); } } + std.sort.sort(i32, result.items, {}, comptime std.sort.asc(i32)); return result; } diff --git a/primes/primes_jit.lua b/primes/primes_jit.lua index 022de012..cbec8093 100644 --- a/primes/primes_jit.lua +++ b/primes/primes_jit.lua @@ -159,11 +159,11 @@ local function find(upper_bound, prefix) queue.push({v, prefix .. ch}) end end + table.sort(result) return result end local function dump(arr) - table.sort(arr) return "[" .. table.concat(arr, ", ") .. "]" end