Announcement

Collapse
No announcement yet.

DIRTYCALL convention 2

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

  • Peter Manders
    replied
    Yes, it should be mentioned that although you can declare LOCAL vars, you should never try to use them with this type of construct.

    It has been pointed out elsewhere by Bob Zale that Basic just works that way. It's better not to even try Hutch's trick if you're uncomfortable or unsure about this.

    I agree that it's probably only safe to use globals and inline assembler.


    Peter.


    ------------------
    [email protected]

    Leave a comment:


  • Steve Hutchesson
    replied
    I agree with both comments, the calling code must have #REGISTER NONE set
    otherwise there can be a register conflict with any register variables
    that are used by the compiler.

    Working outside a stack frame can be risky and in most instances, the normal
    sub/function method of calling remote code is the best way to do it. The
    purpose of bypassing the normal method was to avoid the stack overhead for
    highly recursive calls where the overhead slowed down the execution.

    The use of GLOBAL variables is safe as it does not use the stack and there
    should be no difference from making a CALL/RET within a normal function, the
    address of the label in the DLL function does not change so the call is just
    another in memory jump with the return value on the stack like normal.

    Still, its an interesting way of sneaking some extra performance with highly
    recursive calls in a language that has enough grunt to actually do it.

    Regards,

    [email protected]

    ------------------

    Leave a comment:


  • Tom Hanlin
    replied
    The problem here is that PowerBASIC code requires the stack to be set up
    properly. It is likely to be safe to use DIRTYCALL if the called function
    is entirely in assembly language. If you use any BASIC code, you run the
    risk that some runtime routine will overwrite the stack at a location it
    expected to be able to use.

    ------------------
    Tom Hanlin
    PowerBASIC Staff

    Leave a comment:


  • Semen Matusovski
    replied
    I tested the same approach in EXE.
    Results are nice, but how safety is it ?
    With Register None - probably, yes; without - very doubt.

    Code:
       #Compile Exe
       #Register None
       #Dim All
       #Include "Win32Api.Inc"
       
       %WithCode = %False
       %cntr = 10000000
    
       Global var1 As Long
       Global var2 As Long
       Global var3 As Long
       Global var4 As Long
       Global retv As Long
      
       Sub No1
       
    LoadHere:
    
    $If %WithCode
         ! mov edx, [eax]
         ! mov var1, edx
    
         ! mov edx, [eax+4]
         ! mov var2, edx
    
         ! mov edx, [eax+8]
         ! mov var3, edx
    
         ! mov edx, [eax+12]
         ! mov var4, edx
         
      
         retv = var1 + var2 + var3 + var4
    $EndIf
         ! ret
    ' ----------------------------
       End Sub
    
       Sub No2(rc As RECT)
          
    $If %WithCode
         var1 = rc.nLeft
         var2 = rc.nRight
         var3 = rc.nTop
         var4 = rc.nBottom
         retv = var1 + var2 + var3 + var4
    $EndIf
       End Sub
    
       Function PbMain
         Dim TheProc As Dword, rc As RECT, tc As Long, tc1 As Long, cntr As Long
    
         ! LEA EAX, LoadHere
         ! MOV TheProc, EAX
    
         Rc.nLeft = 1
         Rc.nTop = 2
         Rc.nRight = 3
         Rc.nBottom = 4
    
         tc = getTickCount()
    
         cntr = %cntr
    
    lbl1:
         ! lea eax, Rc ; load EAX with address of structure RECT
         ! call TheProc
         ! dec cntr
         ! jnz lbl1
    
         tc1 = GetTickCount()
    
         MessageBox 0, Format$(0.001 * (tc1 - tc), "#.##"), "PassStruct", %MB_OK Or %MB_ICONINFORMATION
    
    
         tc = getTickCount()
    
         cntr = %cntr
         Dim i As Long
         For i = 1 To cntr
         Call No2(rc)
         Next
       
         tc1 = GetTickCount()
    
        MessageBox 0, Format$(0.001 * (tc1 - tc), "#.##"), "PassStruct", %MB_OK Or %MB_ICONINFORMATION
    
    End Function



    ------------------
    E-MAIL: [email protected]

    Leave a comment:


  • Steve Hutchesson
    started a topic DIRTYCALL convention 2

    DIRTYCALL convention 2

    Here is version 2 of DIRTYCALL with a comparison in time to using the
    normal DLL calling technique through the stack. The times show that
    DIRTYCALL is more than twice as fast, even though it has to manually
    dereference the addresses and load 4 global variables with the
    results.

    The speed difference is due to DIRTYCALL having no stack overhead, even
    though it manually converts the values.

    Regards,

    [email protected]

    Code:
      The calling code.
      
                Case  51
      
                TheProc& = PassStruct
      
                Rct.nLeft   = 1
                Rct.nTop    = 2
                Rct.nRight  = 3
                Rct.nBottom = 4
      
                tc& = getTickCount()
      
                cntr& = 100000000
      
                lbl1:
                  ! lea eax, Rct    ; load EAX with address of structure RECT
                  ! call TheProc&
                  ! dec cntr&
                  ! jnz lbl1
      
                tc1& = GetTickCount() - tc&
      
                MessageBox hWin,ByCopy str$(tc1&),"PassStruct", _
                           %MB_OK or %MB_ICONINFORMATION
      
      
                Case  52
      
                tc& = getTickCount()
      
                cntr& = 100000000
      
                lbl2:
                  NormalCall 1,2,3,4
                  ! dec cntr&
                  ! jnz lbl2
      
                tc1& = GetTickCount() - tc&
      
                MessageBox hWin,ByCopy str$(tc1&),"NormalCall", _
                           %MB_OK or %MB_ICONINFORMATION
      
      The two DLL functions for comparison.
      
      '##########################################################################
      
      FUNCTION PassStruct ALIAS "PassStruct" () EXPORT as LONG
      
          ! jmp over_it
        ' ----------------------------
        ' struct address in EAX
        ' ----------------------------
          LandHere:
      
          GLOBAL var1 as LONG
          GLOBAL var2 as LONG
          GLOBAL var3 as LONG
          GLOBAL var4 as LONG
          GLOBAL retv as LONG
      
          ! mov edx, [eax]
          ! mov var1, edx
      
          ! mov edx, [eax+4]
          ! mov var2, edx
      
          ! mov edx, [eax+8]
          ! mov var3, edx
      
          ! mov edx, [eax+12]
          ! mov var4, edx
      
        ' ------------------------------------
        ' commented out to test calling speed
        ' ------------------------------------
          ' retv = (var1 * var2) ^ (var3 + var4)
          ' ! mov eax, retv
      
          ! ret
        ' ----------------------------
          over_it:
      
          FUNCTION = CodePtr(LandHere)
      
      END FUNCTION
      
      ' #########################################################################
      
      FUNCTION NormalCall ALIAS "NormalCall" (ByVal a as LONG, _
                                              ByVal b as LONG, _
                                              ByVal c as LONG, _
                                              ByVal d as LONG) EXPORT as LONG
      
        ' -------------------------------------
        ' empty function to test calling speed
        ' -------------------------------------
      
          FUNCTION = 0
      
      END FUNCTION
      
      ' #########################################################################
    [This message has been edited by Steve Hutchesson (edited February 09, 2001).]
Working...
X
😀
🥰
🤢
😎
😡
👍
👎