I have been working to improve the performance of some code that processes large amounts of text, a large portion of which is turned into numerics, and I wondered if it might make sense to replace API1’s trusty old Val() with API2’s shiny new String.ToDouble. Sure enough, there’s a significant performance difference between them. Running a loop 100,000 times produces the following results:
API1 - Val(): 10,540 milliseconds
API2 - String.ToDouble: 52,966 milliseconds
That’s a nearly perfect 5X difference. The API1 call is 5 times faster than the API2 call.
My code already uses Val(), so no further action is needed on this project – but this calls into question what should be considered best practices with regards to API2. Val() and String.ToDouble produce identical numeric results; there are no ergonomic differences between their usages; the only factor String.ToDouble has in its favor is some vague hand-waving about it being the more current terminology for the language and that API1 will go away some day.
I suspect that the other String.* functions that have static equivalents are equally slow, because in my performance tuning of the project, I’ve discovered that one of the most significant factors is calling a class method: the overhead of making a method call often dwarfs whatever is inside that method to a shocking extent. Flattening the call tree can produce significant performance gains without changing much of the underlying code that does the actual work.
So – what’s to be done? I can’t honestly recommend API2 due to this kind of performance hit that might be lurking behind some functionality. Surely, since these are static calls (the String.ToDouble call is invariant across all instances of the class), the compiler could safely transform them into the internal equivalent of whatever happens when the code calls Val()?
As Ian stated, String.Val() uses US thousands and decimal separators always. So it makes the internal code more efficient not needing being locale aware.
It looks like toDouble does some cleanup of the string parameter before it gets evaluated, which might explain some of the performance hit. At least Val is still a current method.
What if the test using Val included cleaning the string before each execution?
Thanks, everybody. That makes a lot of sense. In my case, the text I am processing is computer generated and always uses a generic ±1.23e45 format so Val does the trick.
My “big test file” for this project reports that it Val’d nearly 100K strings into numbers (both integer and floating point), so every microsecond of performance I can squeeze out of that one function makes a difference.
Var s As String = "1231.12312"
Var d As Double
d = s.Val // API 2
d = Val(s) // API 1
Both are almost equivalent and for 100k values you probably wont notice any time loss.
What would really eat noticeable time would be d = s.ToDouble() which would truncate the value to 1231.0000 in my current computer set to use Portuguese, not US English, and would do it using several microseconds more. I would use Val(s) instead of s.Val only if I were really looking for squeezing clock pulses.
From this you can see the hottest method is in the String.ToDouble() method, which appears to have a lot of string processing inside it (including a call to ReplaceAllText which perhaps should be reported as a separate bug?)
I wonder if the ReplaceAllText is converting commas to periods before parsing? I’m sure that could explain part of the 5x slowdown.