The article very clearly says that (unlike normal C behaviour) it's a no-op in release builds of SQLite, because otherwise they get a ~3x slowdown. Does Go compile away those `if` statements?
The article very clearly says that (unlike normal C behaviour) it's a no-op in release builds of SQLite, because otherwise they get a ~3x slowdown. Does Go compile away those `if` statements?
The HN guidelines specifically request that we not make comments like this.
> Please don't insinuate that someone hasn't read an article. "Did you even read the article? It mentions that" can be shortened to "The article mentions that."
I was about to delete my comment since it was no longer relevant, but maybe I will leave it here since your reply and edit are such a great example of how to be constructive and civil. (OTOH, let me know if you'd prefer for me to delete it.) Thanks!
That said, you can do this easily enough in Go. Define a package:
package debug
const Debug = true
func Assert(cond bool, msg string) {
if Debug && cond {
panic(msg)
}
}
Have your build system generate the file that contains Debug = true. In optimization mode, make it Debug = false. As it is const, the compiler will propagate it, Assert will be inlined and the dead code removed. If you don't like relying on the compiler, have your code generator generate empty Assert functions. If you are working in a very bad code base that catches all panics, you'll need an extra 5 lines of code in the implementation of Assert, and your stack traces won't be pretty. (Roughly: put the condition check in a goroutine, block the Assert call on a channel signal from that goroutine.) I'd recommend the first project be to remove those panic catch-alls.But really, all of this is only appropriate when you are far out in the weeds. Ship your program with those asserts. If they are too slow for production they are too slow for development.
https://play.golang.org/p/Ts6QqRrWwuQ
I couldn't get that to not trigger the lock.
[later] I could get it to remove it if you pass in a func instead.
(1) One test uses compiler and preprocessor options set up to measure coverage. This is not so much a test of SQLite itself as it is a test of the test logic, to verify that the testing provides 100% MC/DC.
(2) The second test runs with assert(), ALWAYS(), and NEVER() enabled. This is like a unit test. It verifies internal assumptions and state, at the expense of running 3x slower.
(3) Finally, we build as for delivery and test once again. This is the string test, where we "test what we fly".
All three test runs must give the same answer (modulo performance) before a release.
During day-to-day development work, we usually run (2), but occasionally toss in a (1) or (3) just to confirm that we haven't introduced any gaps in test coverage, or bugs that are masked by the debugging logic.
So you are correct that you should not do all your testing using one configuration and then deliver a different build. But that does not mean you can't have extra logic in your code that helps do unit testing and debugging during development and which is excluded from release builds. You just need to make sure that you rerun all tests in the release configuration.
https://www.zdnet.com/article/sqlite-bug-impacts-thousands-o...
This stuff doesn't happen in Go and Rust programs.
I'd rather have Go's guarantees than C's.
As an example, checking errors on GPU is enormously slow because you stall the CPU until the hardware FIFO drains every time you do. You can't ship software that checks for errors after issuing each DirectX or OpenGL call. But writing graphics code is also really annoying without a debug mode that checks for errors after each render command, because the failure mode of graphics programs tends to be "black screen" or "screen full of garbage".
Baking that into the language seems like it just replaces Go with that meta-language and hides the Go underneath like how "C" is really CPP-lang with a C compiler underneath.