Besides @Stefan_Salewski's comments, it also bears mentioning that A) you need
to be careful to compile the Nim with `-d:danger` before making Nim performance
conclusions, B) `--gc:arc` may also help but may or may not be in your
particular version of Nim and C) the algorithm in your Nim case is not quite
the same - in the Go you just do an unconditional append and then a second loop
while in the Nim you do a triple lookup when present and double lookup when
missing . Avoiding string creation with the slicing and doing a more direct
translation of the Go would look like:
func smallestRepr(arg: string): string =
let doubled = arg & arg
result = arg
var slice = result # I *think* I have this right but have not tested..
for i in 1 .. arg.high:
copyMem slice[0].addr, doubled[i].addr, arg.len
if slice < result:
result = slice
proc main() =
var seen = initTable[string, seq[string]]()
for word in paramStr(1).lines:
let key = word.smallestRepr
seen.mgetOrPut(key, @[]).add word
for key, words in seen:
if words.len == 4: echo words
main()
Run
At least in the past when I've benchmarked things, Go's tables were much slower
than Nim's.
In the past I have noticed that, at least with the gcc backend, you can get a
big boost (near 2X) from profile-guided optimization (`gcc -fprofile-generate
...; Run sample prog; gcc -fprofile-use ...`) where the `...` s would be all
the C files Nim wants to compile and the sample prog will probably be named
`a.out`. (Usually everything in `~/.cache/nim/r/` on Unix, but visible via `nim
c --verbosity:2` everywhere.)