Congrats, that's really an achievement !
OpenMP is easy, you can use it like this, the following defines a new -d:openmp
compilation flag:
when defined(openmp):
{.passC: "-fopenmp".}
{.passL: "-fopenmp".}
# Note: compile with stacktraces off or put
# when defined(openmp): {.push stacktrace: off.}
# and
# when defined(openmp): {.pop.}
# around OpenMP proc.
# Heap allocation within a loop will crash OpenMP
# Nim stacktraces allocate a string :/
# Also avoid yourseq.add within an OpenMP loop as that could trigger the GC.
# Preallocate and use yourseq[i] = foo
var foo = newSeq[int](1000)
{.push stacktrace: off.}
for i in 0 || foo.len - 1:
foo[i] = i
{.pop.}
# Alternatively
{.push stacktrace: off.}
for i in `||`(0, foo.len - 1, "simd if(ompsize > 500)"):
foo[i] = i
{.pop.}
# simd will tell the compiler to use the SSE/AVX instructions
# if(ompsize > 500) will only trigger OpenMP if the loop is bigger than 500
# There are lots of other possibilities
Note that OpenMP is sometimes a bit of a hard beast to tame if you work on
structures less than 64 bytes (the size of a cache line:
The work is split on several threads and threads should not work on the same
variable (or use OpenMP atomics) and you might also encounter false
sharing/cache invalidation: if you have an array of 8 integers, it fits in a
cache line (64B), a thread trying to update it will invalidate the cache line
for all other threads and ad repetitum. Result will be slower than single
threaded.