API Declarations...just curious.

L

Lance

Hi all,

I'm curious as to the technicalities of why a particular method declaring and calling is
faster than the other. The C syntax is:

\\\\\
BOOL PathMatchSpec(
LPCSTR pszFile,
LPCSTR pszSpec
);
\\\\\

In VB2005, I'm declaring and then calling an API function as such:

\\\\\
Private Declare Auto Function PathMatchSpec Lib "shlwapi" _
(ByVal pszFileParam As IntPtr, _
ByVal pszSpec As IntPtr) As Boolean

Private Function MatchSpec(ByVal sFile As String, ByVal sSpec As String) As Boolean
Dim FilePtr As IntPtr = Marshal.StringToHGlobalAuto(sFile)
Dim SpecPtr As IntPtr = Marshal.StringToHGlobalAuto(sSpec)
Dim Match As Boolean
Match = PathMatchSpec(FilePtr, SpecPtr)
Marshal.FreeHGlobal(FilePtr)
Marshal.FreeHGlobal(SpecPtr)
Return Match
End Function
\\\\\

This (the above example) runs nearly 20% faster over the course of about 190,000 calls
than declaring and calling it as such:

\\\\\
Private Declare Auto Function PathMatchSpec Lib "shlwapi" _
(ByVal pszFileParam As String, _
ByVal pszSpec As String) As Boolean

Private Function MatchSpec(ByVal sFile As String, ByVal sSpec As String) As Boolean
Return PathMatchSpec(sFile, sSpec)
End Function
\\\\\

With all the marshaling in the first example, I would have thought there would have been
some significant overhead going on compared to the second example.
 
T

Tom Shelton

Lance said:
Hi all,

I'm curious as to the technicalities of why a particular method declaring and calling is
faster than the other. The C syntax is:

\\\\\
BOOL PathMatchSpec(
LPCSTR pszFile,
LPCSTR pszSpec
);
\\\\\

In VB2005, I'm declaring and then calling an API function as such:

\\\\\
Private Declare Auto Function PathMatchSpec Lib "shlwapi" _
(ByVal pszFileParam As IntPtr, _
ByVal pszSpec As IntPtr) As Boolean

Private Function MatchSpec(ByVal sFile As String, ByVal sSpec As String) As Boolean
Dim FilePtr As IntPtr = Marshal.StringToHGlobalAuto(sFile)
Dim SpecPtr As IntPtr = Marshal.StringToHGlobalAuto(sSpec)
Dim Match As Boolean
Match = PathMatchSpec(FilePtr, SpecPtr)
Marshal.FreeHGlobal(FilePtr)
Marshal.FreeHGlobal(SpecPtr)
Return Match
End Function
\\\\\

This (the above example) runs nearly 20% faster over the course of about 190,000 calls

Can you please post a complete example of this (including your method
of timing)? I am not seeing much of a difference at all. Over a
190,000 calls I'm only seeing about a .00008 second difference...
 
L

Lance

Hi Tom,

I knew you'd be the first to reply to an API question :)

My method of timing uses:

\\\\\
Private Declare Auto Function GetTickCount Lib "kernel32" () As Int32
\\\\\

right before and after the start of the loop, then calculates the diference.

BTW, I'm an idiot and I missposted something: 190,000 returned True from the function
call, but the function iteself was called 1,040,528 times. Also, LAN conditions are
likely effecting this as well; the 20% figure I gave earlier is an average over the course
of testing on different days at different times.

FWIW, the loop is not a test loop (i.e., x = 1 to 190000) but a loop that includes
searching for all files (*.*) on a network of which 190,000 fit the sSpec and return True.
Also, the sSpec string contains 44 different file types (i.e. "*. txt; *.log; *.
doc;....." etc.). I suppose these factors may effect the results.

As for the rest of the code, it's a VB.Net-ized version of
http://vbnet.mvps.org/index.html?code/fileapi/recursivefiles_minimal_multiple.htm. Not
all that much has changed from the code you see on that page, except for the types and the
declarations (which you provided in a previous answer to a posting of mine), including the
variations I posted in my original message.

Lance
 
M

Mattias Sjögren

Lance,

I don't think it's a very good idea to involve network IO in your
benchmark. There are so many other factors that affect performance
when you involve a network connection (such as caching, network load
etc).

I tried code similar to yours on different declarations of another API
- lstrcmp - that has almost the same signature as PathMatchSpec.

Declare Auto Function lstrcmp1 Lib "kernel32.dll" Alias "lstrcmp"
(ByVal lpString1 As String, ByVal lpString2 As String) As Integer

Declare Auto Function lstrcmp2 Lib "kernel32.dll" Alias "lstrcmp"
(<MarshalAs(UnmanagedType.LPTStr)> ByVal lpString1 As String,
<MarshalAs(UnmanagedType.LPTStr)> ByVal lpString2 As String) As
Integer

Declare Auto Function lstrcmp3 Lib "kernel32.dll" Alias "lstrcmp"
(ByVal lpString1 As IntPtr, ByVal lpString2 As IntPtr) As Integer

For 1,000,000 iterations timed with the .NET 2.0 StopWatch class I got
the following results

1: 882 msec
2: 198 msec
3: 1525 msec

So in this case the clear winner is declaring the parameters as String
and adding the MarshalAs(LPTStr) attribute to avoid VB's default
MarshalAs(VBByrefStr) which causes unnecessary copying to preserve
classic VB behaviour. The third, DIY method is by far the slowest like
I would expect, since it involves the most managed/native transitions.


Mattias
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Top