bmx-ng / bcc

A next-generation bcc parser for BlitzMax
zlib License
33 stars 13 forks source link

NoDebug - enable for blocks/loops too #602

Closed GWRon closed 1 year ago

GWRon commented 1 year ago

There is the NoDebug keyword which you can append next to functions to disable "debugging" for them.

With the "debug builds" being magnitudes slower than release builds - depending on your code structure (lot of for loops and the likes) I saw eg. a 10 times performance penalty, it would be nice to have fine grained possibilities to mark code as "safe" to improve performance even during debugging.

I am not sure if it is feasible to do but it seems to me a good idea to have stuff like:

Local sum:Int
For local x:Int = 0 until 1000 nodebug 'disable debugging info generation for the complete block
  For local y:int = 0 until 1000
     sum :+ y
  Next
Next
'debugger is active here again - in debug builds of course

Else this code above will lead to 1.000.000 debug frames being produced (so it can display the right stack information when something happens) - or not?

And while I have code which I know "works" flawless, I might not always be able to guarantee it for the whole code in a function (eg when it works with objects instead of some primitives).

Codewise "TBlockDecl" seems to be for "if-then, loops, ..." and has access to the information - and therefor could possibly disable debug code generation rather easily.

GWRon commented 1 year ago

Regarding "performance":

SuperStrict
Framework Brl.StandardIO

Function Test:Int() ' nodebug
    Local result:Int = 0
    For Local i:Int = 0 Until 1000
        result :+ 1
    Next
    Return result - 999
End Function

Local t:Int = MilliSecs()
Local sum:Int
For Local i:Int = 0 Until 100000
    sum :+ Test()
Next
Print (MilliSecs() - t)

With noDebug it runs 30ms for me, without it requires 2900ms here. The Test() function might contain stuff worth to be protected by debugging - but the simple for loop there will for sure "work" without requiring debugger protection - yet the for loop is what creates 2840ms overhead (the function call itself would lead to 60ms compared to 30ms when covered by the debugger)

GWRon commented 1 year ago

Dunno if it was worth discussion replacing the debugger with some better performing one - cannot imagine how "costly" it would be to improve the overall debugger experience. Assume the debugger was created in legacy ... and now we are transpiling to C. So maybe this allows shortcuts (dunno what GCC offers). I mean: maybe we do more stuff in the current debugger functionality than needed.

Also assume that a debugger rewrite might allow for stuff like live added breakpoints or similar.

HurryStarfish commented 1 year ago

And while I have code which I know "works" flawless, I might not always be able to guarantee it for the whole code in a function (eg when it works with objects instead of some primitives).

Wouldn't it be reasonable to put those different parts into separate functions then? That would probably be much easier than the work required to build "partially-debug" functions into the compiler for the rare cases where it would be necessary.

GWRon commented 1 year ago

Not all functions can easily be split apart. Or it requires passing around a lot of temporary variables used alongside the "long" function.

If stuff only is needed inside a single function and not repeated..it sounds odd to artificially split it apart just to allow marking some stuff as "no debug".

HurryStarfish commented 1 year ago

But how often do you have a legitimate performance problem that requires you to exempt code from debugging and you have code in the same function that you still want to debug and those different parts of code are connected in a way that makes them hard to take apart (even though you already have the NoDebug part in a separate block)? I can't say I've ever run into a situation like that, and find it hard to imagine one; do you have real-world examples?

GWRon commented 1 year ago

It just needs some for loops which do work in a "pre-checked environment" ..you would prefer running them as nodebug. Nonetheless the rest of that function might require some guarding.

Also I thought as tdecl contains isNoDebug() already I thought it would be rather easy to add support for blocks too.

Of course this all would be neglectable if the debugger would be better performancing at all. Nodebug is more of a band aid for now.

HurryStarfish commented 1 year ago

Well, it would be better to avoid band-aids for language features unless necessary. So what situations have you encountered where it wasn't possible to move the critical loop into another function?

GWRon commented 1 year ago

The band-aid exist already - as eg TPixmap uses "nodebug" for the "for loop"-heavy parts. So my "request" is just to extend the functionality to cover more than just function. If I stumble over code in my codebase I will happily add it to here.

As said following a strict rule to avoid functions "longer than X lines" is not always the best - if functionality is not used anywhere else then there should not be any need to split a big function into multiple functions just to be able to "nodebug" parts of them. For stuff being "reused" here and there I am with you about splitting it into little functions (and there then using "nodebug" if feasible)

HurryStarfish commented 1 year ago

The band-aid exist already - as eg TPixmap uses "nodebug" for the "for loop"-heavy parts.

Yeah, but at least it's only for files and as a modifier for functions. Adding modifiers to loops like that would be a completely new kind of syntax, for something that will probably never even be needed...

As said following a strict rule to avoid functions "longer than X lines" is not always the best - if functionality is not used anywhere else then there should not be any need to split a big function into multiple functions just to be able to "nodebug" parts of them.

I never said anything about "functions longer than X lines", but splitting different functionality into different functions is a good idea anyway, whether they're used in multiple places or not.

GWRon commented 1 year ago

Whats the benefit of splitting a "big function" into smaller ones - if the functions are ever called in one order/configuration (so the same as if it was one big function)? Would GCC optimize it differently? Readability could be provided by whitespace, comments, ... too.

I never said anything about "functions longer than X lines",

You didn't but it was a common "rule" to identify "doing it all"-functions or similar.

for something that will probably never even be needed...

As said I thought it might be easy to add (that the foundation is already there and it just requires some more lines so that the specific debug commands are not placed inside the generated C code.

My intention is not to open up a new can of worms.

HurryStarfish commented 1 year ago

Whats the benefit of splitting a "big function" into smaller ones

It keeps your code better organized and more maintainable. A function signature gives the code inside it a name and documents what data goes in and out of it. That makes it easier to understand and to rearrange or reuse later, it you find that you want to modify it at some point.

As said I thought it might be easy to add (that the foundation is already there and it just requires some more lines so that the specific debug commands are not placed inside the generated C code.

Alright, but even if the foundation might be there in the (current implementation of) the compiler, it really isn't in terms of syntax. While NoDebug on a function or method is just another modifier like Final etc., having modifiers on a loop would be unprecedented and it doesn't seem justified to me to invent new syntax just for something like this. Being easy to add alone isn't a good reason to add something if it complicates the language for no real gain.

GWRon commented 1 year ago

I do not agree with "keeps your code better organized and more maintainable" for all scenarios. But with the syntax you got a valid point (thought it could somehow be added like step - am not knowing about an equivalent for "if then").

I thought about having conditionals to "configure" the compiler ... but I guess this is no valid option either.