Announcement

Collapse
No announcement yet.

Should the GLOBAL statement be done away with?

Collapse
X
 
  • Filter
  • Time
  • Show
Clear All
new posts

  • #21
    Michael,

    To call a function just to read one Long value, would probably take twice as long, possibly much more. I would guess likely 5 times or better longer.

    You have to push values to the stack, make a jump (more like GOSUB) which requires saving the previous position, then get your value and put it on the stack, jump back to the previous location, pop the stack to get your value.

    For a simple memory read of what should be global data, this is overkill compared to just moving a value from one memory location (global variable) to another (local variable).

    In time critical code (ie. a loop which reads global data), this could slow down code as much as four fold, IMO.
    Chris Boss
    Computer Workshop
    Developer of "EZGUI"
    http://cwsof.com
    http://twitter.com/EZGUIProGuy

    Comment


    • #22
      I often use GOTO within a function where too many nested IF/THEN and SELECT CASE blocks prevent using a simple EXIT to jump out. I usually have an Abort: label at then end of many of my functions to break out of the process and cleanup before exiting the function. I probably could put the cleanup code in every branch of the structures, but why have the extra overhead when it does the same for all cases and is much easier to read.

      And in pretty much any app where I use threads I use them. Usually you have data to work with and need some type of control of the thread. All you have is 32bits you can pass to the function and either way it will be a memory address to the data which would need to be Static/Global or the control variable or handle to the Object for the Wait API calls...which again would be Static/Global.
      sigpic
      Mobile Solutions
      Sys Analyst and Development

      Comment


      • #23
        Using JC's code for a function to store a global value, I wrote a test program that compared that function to simply accessing a Global variable.

        Here is the code:

        Code:
        #COMPILE EXE
        #DIM ALL
        DECLARE FUNCTION QueryPerformanceCounter LIB "KERNEL32.DLL" ALIAS "QueryPerformanceCounter" (lpPerformanceCount AS QUAD) AS LONG
        DECLARE FUNCTION QueryPerformanceFrequency LIB "KERNEL32.DLL" ALIAS "QueryPerformanceFrequency" (lpFrequency AS QUAD) AS LONG
        FUNCTION PTIMER() AS QUAD
            LOCAL ATime AS QUAD, TM AS QUAD
            STATIC TFlag&, TestFreq AS QUAD
            IF TFlag&=0 THEN
                IF QueryPerformanceFrequency(TestFreq) THEN     ' use performance timer
                    TFlag&=1
                END IF
            END IF
            IF TFlag&=1 THEN
                QueryPerformanceCounter ATime
                TM=ATime*100/TestFreq
            END IF
            FUNCTION=TM
        END FUNCTION
        GLOBAL App_MyData&
        FUNCTION GetVar1(OPT param AS LONG)AS LONG
            STATIC Var1 AS LONG
            IF VARPTR(param) THEN Var1 = param
            FUNCTION = Var1
        END FUNCTION
        FUNCTION PBMAIN () AS LONG
            LOCAL T$, MaxLoop&, TM1#, TM2#, TM3#, TM4#
            LOCAL CT1 AS QUAD
            LOCAL CT2 AS QUAD
            LOCAL CT3 AS QUAD
            LOCAL CT4 AS QUAD
            
            REGISTER N&, X&
            App_MyData&=5
            GetVar1(5)
            
            MaxLoop&=100000000
            CT1=PTIMER
            FOR N&=1 TO MaxLoop&
                  X&=App_MyData&
            NEXT N&
            CT1=PTIMER-CT1
            CT2=PTIMER
            FOR N&=1 TO MaxLoop&
                 X&=GetVar1
            NEXT N&
            CT2=PTIMER-CT2
            CT3=PTIMER
            FOR N&=1 TO MaxLoop&
                  App_MyData&=5
            NEXT N&
            CT3=PTIMER-CT3
            CT4=PTIMER
            FOR N&=1 TO MaxLoop&
                 GetVar1(5)
            NEXT N&
            CT4=PTIMER-CT4
            T$=""
            TM1#=CT1/100
            T$= T$+"Global Read Time was       "+STR$(TM1#)+" for"+STR$(MaxLoop&)+" iterations !"+CHR$(13)+CHR$(10)
            TM2#=CT2/100
            T$=T$+"Function Read Time was  "+STR$(TM2#)+" for"+STR$(MaxLoop&)+" iterations !"+CHR$(13)+CHR$(10)
            TM3#=CT3/100
            T$=T$+"Global Write Time was       "+STR$(TM3#)+" for"+STR$(MaxLoop&)+" iterations !"+CHR$(13)+CHR$(10)
            TM4#=CT4/100
            T$=T$+"Function Write Time was  "+STR$(TM4#)+" for"+STR$(MaxLoop&)+" iterations !"+CHR$(13)+CHR$(10)
            IF TM1#>0 THEN
                 TM1#=ROUND((TM2#/TM1#),3)
                 T$=T$+"Global Read is "+STR$(TM1#)+" times faster than Function"+CHR$(13)+CHR$(10)
            END IF
            IF TM3#>0 THEN
                 TM3#=ROUND((TM4#/TM3#),3)
                 T$=T$+"Global Write is "+STR$(TM3#)+" times faster than Function"+CHR$(13)+CHR$(10)
            END IF
            MSGBOX T$
        END FUNCTION
        On my computer (2.5 ghz CPU) the test results were:

        The Global variable READ was over 7 times faster than the function.
        The Global variable WRITE was over 10 times faster than the function.


        That is significant, if you are writing time critical code !
        Chris Boss
        Computer Workshop
        Developer of "EZGUI"
        http://cwsof.com
        http://twitter.com/EZGUIProGuy

        Comment


        • #24
          The Global variable READ was over 7 times faster than the function.
          The Global variable WRITE was over 10 times faster than the function
          Gross numbers for 10000000 iterations?

          Call me casual about this, but seven or ten times faster than .0002 seconds total is not what I would consider a "Stop the presses!" revelation.

          I don't really want the numbers. I find these "empty loop" so-called "speed tests" pretty much useless and I could not resist the opportunity to point this out. Of course, I'm not allowing for the possibility that someday an empty-loop piece of software is going to put a roof over my head or food on my table, but I'll pay off if that happens.

          MCM
          Michael Mattias
          Tal Systems (retired)
          Port Washington WI USA
          [email protected]
          http://www.talsystems.com

          Comment


          • #25
            The use of GLOBALs and STATICs is addressed in the Programming Reference undr Variables and Variable Scope in the subtopic LOCAL, GLOBAL and STATIC considerations. It's well worth reading and understanding The following excerpt, with added emphasis shows some of the rational global usage embraces:
            All LOCAL variables are stored on the stack frame of the Sub/Function in which they are defined. Therefore, the address of local variables may vary with each invocation of the Sub/Function in which they are defined (because they are created each time the Sub/Function is executed, and destroyed upon exit). It is unsafe to return a to a local variable, since the storage for that variable is released when the Sub/Function ends.
            Conversely, GLOBAL and STATIC variables are stored in the main data memory area, so their address stays constant for the duration of the program module, so returning a pointer to a GLOBAL or STATIC variable is quite safe.
            The point is that a global or static has a compiled offset location that reserves the address for the vars value, descriptor table or handle depending on the var's type. Fixed and fast access from anywhere in the .exe module. Speed because it does not have to be pushed and popped off the stack frame as a local would.

            So, I quite agree with Chris's remarks on GLOBALs, GOTO and GOSUB. The latter two commands closely follow the actual assembly and thence cpu instruction code "jumps" that one's BASIC source compiles to.

            The main problem some have had with globals is naming conflicts. Some would call that pilot error Therefore avoiding such errors is often done by employing naming conventions. Examples have been presented many times over the last several years. They can be as simple as prefacing the var. name with "g" or "g_" or similar. The actual technique is individually what what one desires, that is if they are not employing one of the recommended methods from MS, et.al.
            Rick Angell

            Comment


            • #26
              Chris,
              You are correct. In my test Globals are about 4 times faster but let's
              put this in perspective with my little piece of code below.( You were faster
              but I just had to post it.)
              This shows (on my computer 3gz P4) a difference of 8 milisecs on
              1,000,000 calls . Where would this make a difference?
              I'm afraid your speed argiument just does not wash under normal windows
              operation.
              In time critical code Yes by all means.

              Re:MCM My loops are not empty(almost but not quite )

              James


              Code:
              #COMPILE EXE
              #DIM ALL
              
              DECLARE FUNCTION QueryPerformanceFrequency LIB "KERNEL32.DLL" ALIAS "QueryPerformanceFrequency" (lpFrequency AS QUAD) AS LONG
              DECLARE FUNCTION QueryPerformanceCounter LIB "KERNEL32.DLL" ALIAS "QueryPerformanceCounter" (lpPerformanceCount AS QUAD) AS LONG
              DECLARE FUNCTION SetPriorityClass LIB "KERNEL32.DLL" ALIAS "SetPriorityClass" (BYVAL hProcess AS DWORD, BYVAL dwPriorityClass AS DWORD) AS LONG
              DECLARE FUNCTION GetCurrentProcess LIB "KERNEL32.DLL" ALIAS "GetCurrentProcess" () AS LONG
              %REALTIME_PRIORITY_CLASS     = &H00000100
              %NORMAL_PRIORITY_CLASS       = &H00000020
              
              GLOBAL var2 AS LONG
              
              
              GLOBAL var2 AS LONG
              
              FUNCTION GetVar1(OPT param AS LONG)AS LONG
                  STATIC Var1 AS LONG
                  DIM Var2 AS LONG
                  IF VARPTR(param) THEN
                      Var1 = param
                  END IF
                  FUNCTION = Var1
              
              END FUNCTION
              
              SUB OkLetsTestIt()
                LOCAL Var3,Var4 AS LONG
                LOCAL dTimeIn,dTimeOut,dTicksSecond AS QUAD
                LOCAL  dTotal1,dTotal2 AS DWORD
                LOCAL i AS LONG
                Var3 = 44
              
              
                QueryPerformanceFrequency dTicksSecond
              
                SetPriorityClass GetCurrentProcess(), %REALTIME_PRIORITY_CLASS
                QueryPerformanceCounter dTimeIn
                FOR i = 1 TO 1000000
                  Var4 = Var3 +  GetVar1
                NEXT i
                QueryPerformanceCounter dTimeOut
              
                SetPriorityClass GetCurrentProcess(), %NORMAL_PRIORITY_CLASS
              
              
                dTotal1 = dTimeOut - dTimeIn
                dTotal1 = ((dTotal1/dTicksSecond)*1000)
                PRINT "Total Time For FuncCall= "; FORMAT$(dTotal1)
              
                
                SetPriorityClass GetCurrentProcess(), %REALTIME_PRIORITY_CLASS
                QueryPerformanceCounter dTimeIn
                FOR i = 1 TO 1000000
                  Var4 = Var3 +  Var2
                NEXT i
                QueryPerformanceCounter dTimeOut
              
                SetPriorityClass GetCurrentProcess(), %NORMAL_PRIORITY_CLASS
              
                dTotal2 = dTimeOut - dTimeIn
                dTotal2 = ((dTotal2/dTicksSecond)*1000)
                PRINT "Total Time For GLOBAL= "; FORMAT$(dTotal2)
              
              
                PRINT "Wow "; dTotal1-dTotal2;" miliseconds on 1,000,000 calls
                
              
                
                
              END SUB
              
              FUNCTION PBMAIN () AS LONG
                 LOCAL Var1 AS LONG
                 GetVar1(22)
                 Var2 = 22
                 
                OkLetsTestIt
              
                 WAITKEY$
              
              
              END FUNCTION

              Comment


              • #27
                Greetings ....

                To Chris Boss - Thank you for posting the Global-vs-Function demo code. When viewing the results, it is easy to see what they are saying in terms of globals vs functions.

                I suppose if I needed 100 million iterations of something rather quick, I might consider using some globals rather than functions. But since I rarely go beyond 100 to 1,000 iterations of anything, I'll stick with functions.

                Thanks also to JCFuller for his demo code.
                Last edited by Frank Ferrell; 14 Dec 2007, 12:42 PM.

                Comment


                • #28
                  I never said that speed was important on all instances of using Globals.

                  The theme of this thread was that Globals are not needed anymore.

                  My post demostrates how valuable globals are for time critical code.

                  Now you may not think that there are many instances where that many loops are needed, so my point is mute.

                  The point is that globals are significantly faster.

                  In real use, more than one global variable may be used in loop code, so with each new variable used a speed hit would occur in every iteration.

                  There is code where millions of iterations do occur.

                  For example, in EZGUI's Sprite engine, where millions of iterations occur every second, I had to use global arrays to speed things up.

                  So if speed is not the issue, the only other issue for not using a Global variable is that it is somehow not proper coding style or it somehow obsolete.

                  Thats makes no sense at all.

                  Why would a function be better coding that a global variable ?

                  The only downside to Global variables comes when one uses poor naming for variables. To make a variable called X or Y global is dangerous because such names are often used for local variables.

                  The solution is simple.

                  Always use a consistant prefix for all global variables!

                  One prefix I like to use is:

                  App_

                  When I see code like:

                  X&=Y*App_MyVariable

                  I immediately know the variable with the App_ prefix is global.

                  Some like to use a simple prefix like g (ie. gMyVariable).

                  Personally I like to use a larger prefix so it stands out and there is no chance of a mistake recognizing it.

                  By using good variable naming rules (that fit your style of coding), Global Variables not only produce faster code, but more easily readable code.

                  There is no good reason to phase out global variables.

                  Just like some say GOTO or GOSUB are bad coding practices (which they are not), some pick on the poor Global variable as if it is a bad coding style (which it is not).

                  Lastly, just because PC's are so fast today, doesn't mean one shouldn't write code which is as fast as possible. Speed does make a difference.

                  Sure, the apps runs fine on todays fast PC's, but what if someone wanted to (or needed to) run the app on a much slower computer. Its so easy to require users to have the latest hardware, just so our software runs fast on it. Users have rights too, and they shouldn't have to always upgrade their hardware simply to run a new program they buy.

                  When I write an app, I want it to run just as well on a 300 mhz PC (running Windows 95) as it does on a 3 ghz PC (running Vista). Then my customers can use the software on not only new PC's but even older ones they still have.

                  Imagine how much money would be saved by businesses if they could still use modern apps on older PC's (and it runs well). Rather than throw away all the old PC's, they can still have a valuable use.

                  Try running the test program above on an older PC (say below 500 mhz) and see what a difference Global variables are making.
                  Chris Boss
                  Computer Workshop
                  Developer of "EZGUI"
                  http://cwsof.com
                  http://twitter.com/EZGUIProGuy

                  Comment


                  • #29
                    One other consideration in favor of global variables:

                    Time spent coding.

                    If I had to waste time writing a function for every single Global variable I needed, I could end up wasting hours of time.

                    Why write this:

                    Code:
                    FUNCTION MyVar1(OPT param AS LONG)AS LONG
                        STATIC Var1 AS LONG
                        IF VARPTR(param) THEN Var1 = param
                        FUNCTION = Var1
                    END FUNCTION
                    when I could write:

                    Code:
                    GLOBAL MyVar1 AS LONG
                    Also which is more readable code (easier to recognize what it is doing):

                    Code:
                    MyVar1 5
                    or

                    Code:
                    MyVar1 = 5

                    So whats the value in using a Function ?

                    Where I come from time is money, so the more time I have to spend coding the more money it costs (someone).
                    Last edited by Chris Boss; 14 Dec 2007, 01:21 PM.
                    Chris Boss
                    Computer Workshop
                    Developer of "EZGUI"
                    http://cwsof.com
                    http://twitter.com/EZGUIProGuy

                    Comment


                    • #30
                      It's easy to write a full application without globals, all you have to do is define a UDT and pass it around. The only thing each function needs to know is the address of the structure.

                      Code:
                      '------------------------------------------------------------------------------
                      '
                      ' Demonstrates creating multiple independent 'objects' for storage.
                      ' Each object can be passed to a function to be read or modified.
                      '
                      ' One advantage of this method is that the functions are re-entrant,
                      ' and the code is thread safe (after a critical section object is used).
                      ' Also, you can create many of these 'objects' if you require.
                      '
                      ' The exact overhead of passing the pointer between functions is 4 bytes.
                      '
                      '------------------------------------------------------------------------------
                      #Compile Exe
                      #Dim All
                      %USEMACROS = 1
                      #Include "WIN32API.INC"
                       
                      Type MYSETTINGS
                           szName As Asciiz * 128
                           x As Long
                           y As Long
                           z As Long
                      End Type
                       
                      Sub InitValues(ByRef tSettings As MYSETTINGS, ByRef szName As Asciiz)
                        tSettings.szName = szName
                        tSettings.x = 1
                        tSettings.y = 2
                        tSettings.z = 3
                      End Sub
                       
                      Sub DisplayValues(ByRef tSettings As MYSETTINGS)
                        ? tSettings.szName + Space$(5) + Format$(tSettings.x) + Space$(5) + Format$(tSettings.y) + Space$(5) + Format$(tSettings.z)
                      End Sub
                       
                      '------------------------------------------------------------------------------
                      ' Program Start Point
                      '------------------------------------------------------------------------------
                      Function PBMain
                        Local pSet1 As MYSETTINGS Ptr
                        Local pSet2 As MYSETTINGS Ptr
                       
                        ' Create a couple of objects...
                        ' Note: Here, GlobalAlloc is used, but since this is the program start,
                        '       you could also define the structures as MYSETTINGS and skip the
                        '       alloc/dealloc. That method is useful if you know the amount of
                        '       structures beforehand.
                       
                        pSet1 = GlobalAlloc(%GPTR, SizeOf(MYSETTINGS))
                        pSet2 = GlobalAlloc(%GPTR, SizeOf(MYSETTINGS))
                       
                        ' Assign some default data and a name...
                        InitValues(@pSet1, "Global Structure #1")
                        InitValues(@pSet2, "Global Structure #2")
                       
                        ' Display the values...
                        DisplayValues(@pSet1)
                        DisplayValues(@pSet2)
                       
                        ' Upon exit, delete the values...
                        ' Note: You could use an pointer array and a deallocation function
                        '       for this in case you only want to track one pointer.
                        GlobalFree pSet1
                        GlobalFree pSet2
                      End Function
                      Last edited by Kev Peel; 14 Dec 2007, 01:26 PM.
                      kgpsoftware.com | Slam DBMS | PrpT Control | Other Downloads | Contact Me

                      Comment


                      • #31
                        Well, if globals disappear (which I doubt will happen), we can pass the 200
                        globals in a parameter (no, I know the limit) by making a UDT


                        Code:
                        TYPE myGlobals 
                         first as long
                         second as dword
                         ....
                         twohundreth as long
                         END TYPE
                        
                        
                         'in a callback
                        
                        CALLBACK FUNCTION SomethingProc (hndl)
                        DIM gls as myGlobals
                        
                        Call SomethingSweet CBHNDL,MyGlobals, ala,foo,doo,blah,sun,mon,tue,bark
                        
                        
                        END FUNCTION
                        But, since globals won't go away, we don't need to do this. I like globals for some things.
                        Barry

                        Comment


                        • #32
                          To be honest I have never used any form of the example code I posted.
                          It was just to show an alternative to GLOBAL's.
                          I use the method Kev posted with perhaps one GLOBAL for the Message loop if I have multiple modeless dialogs.

                          James

                          Comment


                          • #33
                            Should the GLOBAL statement be done away with?
                            Definitely not!

                            Comment


                            • #34
                              I have suggested some time ago to PB that we should have a feature which if you press a function key, shows all the globals in another colour.

                              This idea could be extended to other features and functions.

                              Another point that I was interested in this thread was some statistics about programming features. Is there a program that analyses your code and gives you all possible statistics? If so, what is it, if not should there be?
                              [I]I made a coding error once - but fortunately I fixed it before anyone noticed[/I]
                              Kerry Farmer

                              Comment


                              • #35
                                Kerry,
                                I'm not sure if this has been updated or not??

                                James

                                http://www.powerbasic.com/files/pub/...ls/pbcodec.zip

                                Comment


                                • #36
                                  Kerry,
                                  Thats a great idea, but why not just have a selectable color/font/stye option for globals and equates instead? I'd vote for that rather than a function key approach

                                  Kev,

                                  Most any global user would not dispute that vars can be passed hither and yon with various techniques as the program (assuming more than a one function app) executes. The problem with "global" is never the global var per se in what I have seen reported over the last several years, the problems seem to narrow down to naming conflicts and for multi-threaded apps avoiding collisions. Both of these can be avoided.

                                  IMO, the interesting example you presented over-simplifies the passing overhead. What it appears on its face to present is the code attempting to create a user defined "data memory area" equivalent. That means a "data memory area" managed separately - vs - letting the compiler do that in its natively efficient manner for a GLOBAL var, where all references to the global var are simply the offset reference address of the var.

                                  The fact that you are going to be "passing" just 4 bytes ignores the fact that a global is not passed, just accessed. So every time you pass such a var pointer to a function (not including callbacks) , it gets PUSHed on that function's stack frame, before it can be accessed. It gets PUSHed every time you call such a function as part of the processing flow. (I'm not adressing other techniques though such as optionals and local statics, put the compiler reserves a stack frame location for an optional anyway, so the push op is always there AIUI.

                                  Additionally, in an app requiring callbacks (even some PB CC efforts do) , you may then have to store first to a retrievable location and in the callback retrieve that pointer to have it available for use there. If the callback services more than one window (more of a PB Win need) , then every time you get to a situation needing the value, it has to be retrieved, unless you retrieved once and assigned to a local static there. So IOW passing will always add more steps to a program than globals for the same target processing across multiple functions.

                                  How about the idea that a TYPE variable is really going to solve all the program wide data access problems here. I'd not personally have any appetite for all the pointer coding inside a function to access an individual data with the appropriate dot member syntax .... if there are 10's or 100's of members. It's bad enough maybe for those with 100's of globals. So unless one is a masochist for typing, I'd aver that a massive TYPE var would be un-attractive. It is to me!.

                                  Then there are the problems added by trying to have a program-wide dynamic array available. That would mean extra code for a TYPE var approach. And finally there is the need to both allocate and release the memory yourself where with globals, equates and statics the compiler never forgets to do it for you.

                                  That said, for most of us that use globals, we'd probably agree that a real-world balance lies somewhere in between. That is we use globals, statics and local variable passing. My globals often involve typed vars and dynamic arrays, not just flags, and simple numeric data or discrete strings. So thanks for the code, but I'll not be going that way soon
                                  Rick Angell

                                  Comment


                                  • #37
                                    What's this, attack of the Style Gestapo?

                                    You don't have to use them if you don't want to, you know!

                                    (((((Globals)))))

                                    Comment


                                    • #38
                                      Nah, those "global vs passing" threads were in the past, like one a year for a while IIRC. This is just for prose practice methinks Most of us know that something leaving the PB BASIC language would be very rare indeed ... since it would tend to break existing source using such a feature.
                                      Rick Angell

                                      Comment


                                      • #39
                                        I think globals are like all the other features that PB offers -- use the right tool for the job. I don't use globals often, but when I do, they are really about the only tool for the job.

                                        For integer values though, the "pigeon holes" offered with the CONTROL SET/GET USER statements offer a reasonable alternative.
                                        Real programmers use a magnetized needle and a steady hand

                                        Comment


                                        • #40
                                          Rick,

                                          I was making a point. A few stated that you cannot write an application without using globals which is simply false.

                                          Yes, I do use globals for the reasons you mention (ie. not having to obtain a memory pointer every time it needs to be accessed). In fact, most of the time I actually use a single global TYPE for a cleaner implementation.

                                          However, I do use the parameter approach seen in my example when writing Windows custom controls and (expecially) library code, where global use is (usually) bad for threading or re-entrancy reasons.

                                          I guess my point is: Globals are good, they are here to stay, but they are by no means essential when writing your application
                                          kgpsoftware.com | Slam DBMS | PrpT Control | Other Downloads | Contact Me

                                          Comment

                                          Working...
                                          X