For a little program to create triangle mesh I have to convert 2-3d vectors (vmath) to strings and write them to file. The code snippet below shows what takes 65% of the run time, it feels unnecessary slow. (Full source of current state)
I read a note on string assignment but using ref string had no significant effect.
some guidance is welcome.
proc povVec2*(a:Vec2):string {.inline.}=
"<" & $a.x & "," & $a.y & ">"
proc povVec3*(a:Vec3):string {.inline.}=
"<" & $a.x & "," & $a.y & "," & $a.z & ">"
for i in 0..<len(vecArr):
vecStr.add(povVec3(vecArr[i]))
normStr.add(povVec3(normArr[i]))
uvStr.add(povVec2(uvArr[i]))
Binary -> decimal conversion is generally very slow. The best thing to do is to not do it at all if possible, but just stay in binary.
If external requirements really force you into ASCII and if you can use the head/tip of nim-devel then you can make your code much faster with -d:nimPreviewFloatRoundtrip. For me the time for prep vectors went from 0.278 sec to 0.053 s or over 5x, while your whole program went from 0.315 sec to 0.088 s.
In the new dragonbox/-d:nimPreviewFloatRoundtrip version, 35% of time still comes from all that string allocating & formatting, backing up my just don't do it at all advice, if that is possible. A similar cost compounds on the other side that has to parse your ASCII.
Binary -> decimal conversion is generally very slow. The best thing to do is to not do it at all if possible, but just stay in binary.
To give you an idea of speed; Generating the same mesh in the same resolution directly in POV-Ray using similar code, without writing the file takes ~30 s here, on a decade old box. With writing the file it takes ~5 s more. Parsing the written ASCII file takes ~2 s. On that old box the Nim version, blush blush, takes 6 seconds. ( I see a solution in an other direction...)
I'll look into the -d:nimPreviewFloatRoundtrip option, that's a substantial imporvement.
@Stefan_Salewski Although I 'only' do 3D work I'll have a thorough poke at your project.
Thanks
try
proc povVec2*(a:Vec2):string {.inline.}=
result = "<"
result.addFloat a.x
result &= ", "
result.addFloat a.y
result &= ">"
This could save atleast a few allocations.
This could save atleast a few allocations.
Thanks.
Could a (closure) iterator and streaming to file work? Just thinking loud.
for i in uvArr:
yield "<"
yield $i.x
yield ","
yield $i.y
yield ">"
Wow I love optimizing things. Most of the time is taken up but the float to string conversion. I tried to eliminate all of the unnecessary copies, index checks and buffer grows:
name ............................... min time avg time std dv runs
var str ........................... 11.591 ms 11.807 ms ±0.133 x423
orignal ........................... 12.601 ms 12.863 ms ±0.171 x388
I only managed to eliminate 1ms, it's not worth the complexity, It's all in the c_sprintf. Because you are dealing with graphics maybe there is a suboptimal but a faster float writer. But still it will not be zero time it will always be up to the float to string converter.
import vmath, benchy
var vecArr: seq[Vec3]
for i in 0 ..< 10000:
vecArr.add(vec3(i.float, i.float*2, 1))
{.push checks:off.}
proc c_sprintf(buf, fmt: cstring): int {.importc:"sprintf", header: "<stdio.h>", varargs.}
proc addFloat32(s: var string, i: var int, f: float32) {.inline,.} =
i += c_sprintf(s[i].addr, "%f", f)
proc addChar(s: var string, i: var int, c: char) {.inline.} =
s[i] = c
inc i
timeIt "var str":
var
s = ""
i = 0
for j in 0 ..< vecArr.len:
s.setLen(i + 128)
let a = vecArr[j]
addChar(s, i, '<')
addFloat32(s, i, a.x)
addChar(s, i, ',')
addFloat32(s, i, a.y)
addChar(s, i, ',')
addFloat32(s, i, a.z)
addChar(s, i, '>')
s.setLen(i)
keep(s)
{.pop.}
# proc `$`*(x: float): string {.magic: "FloatToStr", noSideEffect.}
# ## The stringify operator for a float argument. Returns `x`
# ## converted to a decimal string.
# proc floatToStr(f: float64): string =
# var buffer: array[128, char]
# c_sprintf(addr buffer, "%.16e", f)
# result = ""
# for ch in buffer:
# if ch == '\0':
# return
# add(result, ch)
timeIt "orignal":
proc povVec3(a: Vec3): string {.inline.} =
"<" & $a.x & "," & $a.y & "," & $a.z & ">"
var vecStr = ""
for i in 0 ..< vecArr.len:
vecStr.add(povVec3(vecArr[i]))
keep(vecStr)
-d:danger
this about halves the total time taken. The time spent in the conversion to string seems to creeps up a little towards ~70% of the total execution time.
@planetis @treeform I'll play with your suggestions later.
Thanks.