.NET, ActiveX, & my journey into IL Hell...

  • Thread starter Thread starter Larry Nixon
  • Start date Start date
L

Larry Nixon

I was tasked to build a .NET/C# server-side application that'd answer
calls from a modem bank & allow Zmodem uploads. I was given Sax's
SaxComm8 ActiveX component (already paid for & not much else out there
handles Zmodem; also, new Sax.NET framework STILL doesn't offer Zmodem
support yet).

With the help of VS.Net, I got the Interop & AxInterop DLL's generated
& working & thought all was well. That is, until I saw it under load.
Memory was cool, but CPU usage was at 50% with only 1 instance...&
that's WITH Hyperthreading (99% without)!

I then compiled the Sax component into an old Win32 app to test its
performance against .NET & it was a pig as well. But only half as bad
(w/Hyperthreading: 25%; w/o: 50%). So my next task was to attempt to
trim off as much of the 25% overage as was possible, seeing as I
needed to support 8 lines per server (& never a problem on our old
UNIX server running an ANSI C equilavent).

Anyway, after reading Mr. Nick Wienholt's recommendation for a few
other folks suffering similar performance problems here, I decided to
attempt to remove the run-time security checks/"stack walk" by using
the "SuppressUnmanagedCodeSecurityAttribute" attribute on the class
definitions &/or methods that are called frequently within the
generated Interop assemblies/"ActiveX wrappers" (which is supposed to
move the check into link/JIT time, which is supposed to pep things up
a bit).

Of course, this couldn't be straight forward. As Nick puts it: "A
painful exercise that would be trivial if the AxImp utility had an
'/unsafe' option &/or equivalent like the TlbImp utility does". So, I
had to disassemble these assemblies with the ILDASM utility, add the
attribute manually, & then reassemble the DLLs with the ILASM utility.
Here's how it went:

1.) Used ILDasm to generate MSIL source code for the assembly.
C:\Program Files\Microsoft Visual Studio .NET\FrameworkSDK\Bin>
ILDASM .\Interop.SaxComm8.dll /out=.\Interop.SaxComm8.il
ILDASM .\AxInterop.SaxComm8.dll /out=.\AxInterop.SaxComm8.il
This step also produced a RES file since resources were found in the
assembly (which I needed to include later).

2.) Modified the MSIL code with text editor in the "CLASS MEMBERS
DECLARATION" section by adding:
.custom instance void [mscorlib]
System.Security.SuppressUnmanagedCodeSecurityAttribute::.ctor() =
( 01 00 00 00 )
....in every ".class", ".method", ".event", & ".property" I could
(initially, just to see if performance was improved at all).

3.) Recompiled the MSIL using the ILASM compiler. Since a RES file
was generated, it was compiled back into the assembly during this step
using the /resource option on ILASM.
C:\WINNT\Microsoft.NET\Framework\v1.1.4322>
ILASM /DLL /resource=.\Interop.SaxComm8.res
/key=.\EfileReceiver.snk .\Interop.SaxComm8.il
ILASM /DLL /resource=.\AxInterop.SaxComm8.res
/key=.\EfileReceiver.snk .\AxInterop.SaxComm8.il

And...nothing. Bumpkiss. No improvement in speed &/or CPU usage
what-so-ever.

So my question: What am I doing wrong?

Any help in this area is/will be much appreciated. Especially since
there's not much documentation out there dealing with this stuff (IL
attribute tweaks, ActiveX interop, Zmodem & .NET,...was I set up to
fail or what!?!). Thanks.

- Larry
 
I you get 100% CPU usage w/o hyperthreading and only 50% with
hyperthreading, it means that only one thread is using the CPU (and that it
is using all of it) at a given time.

If you are testing your app with only one active client (and thus one thread
performing real stuff), this is perfectly normal. Don't expect
hyperthreading to give you more in this case.

But if you are testing your app with several active clients and you don't go
over 50% CPU with hyperthreading, then something's wrong. Probably a
synchronization lock that's too high level in your code and only lets one
thread go through.

Bruno.

Larry Nixon said:
I was tasked to build a .NET/C# server-side application that'd answer
calls from a modem bank & allow Zmodem uploads. I was given Sax's
SaxComm8 ActiveX component (already paid for & not much else out there
handles Zmodem; also, new Sax.NET framework STILL doesn't offer Zmodem
support yet).

With the help of VS.Net, I got the Interop & AxInterop DLL's generated
& working & thought all was well. That is, until I saw it under load.
Memory was cool, but CPU usage was at 50% with only 1 instance...&
that's WITH Hyperthreading (99% without)!

I then compiled the Sax component into an old Win32 app to test its
performance against .NET & it was a pig as well. But only half as bad
(w/Hyperthreading: 25%; w/o: 50%). So my next task was to attempt to
trim off as much of the 25% overage as was possible, seeing as I
needed to support 8 lines per server (& never a problem on our old
UNIX server running an ANSI C equilavent).

Anyway, after reading Mr. Nick Wienholt's recommendation for a few
other folks suffering similar performance problems here, I decided to
attempt to remove the run-time security checks/"stack walk" by using
the "SuppressUnmanagedCodeSecurityAttribute" attribute on the class
definitions &/or methods that are called frequently within the
generated Interop assemblies/"ActiveX wrappers" (which is supposed to
move the check into link/JIT time, which is supposed to pep things up
a bit).

Of course, this couldn't be straight forward. As Nick puts it: "A
painful exercise that would be trivial if the AxImp utility had an
'/unsafe' option &/or equivalent like the TlbImp utility does". So, I
had to disassemble these assemblies with the ILDASM utility, add the
attribute manually, & then reassemble the DLLs with the ILASM utility.
Here's how it went:

1.) Used ILDasm to generate MSIL source code for the assembly.
C:\Program Files\Microsoft Visual Studio .NET\FrameworkSDK\Bin>
ILDASM .\Interop.SaxComm8.dll /out=.\Interop.SaxComm8.il
ILDASM .\AxInterop.SaxComm8.dll /out=.\AxInterop.SaxComm8.il
This step also produced a RES file since resources were found in the
assembly (which I needed to include later).

2.) Modified the MSIL code with text editor in the "CLASS MEMBERS
DECLARATION" section by adding:
.custom instance void [mscorlib]
System.Security.SuppressUnmanagedCodeSecurityAttribute::.ctor() =
( 01 00 00 00 )
...in every ".class", ".method", ".event", & ".property" I could
(initially, just to see if performance was improved at all).

3.) Recompiled the MSIL using the ILASM compiler. Since a RES file
was generated, it was compiled back into the assembly during this step
using the /resource option on ILASM.
C:\WINNT\Microsoft.NET\Framework\v1.1.4322>
ILASM /DLL /resource=.\Interop.SaxComm8.res
/key=.\EfileReceiver.snk .\Interop.SaxComm8.il
ILASM /DLL /resource=.\AxInterop.SaxComm8.res
/key=.\EfileReceiver.snk .\AxInterop.SaxComm8.il

And...nothing. Bumpkiss. No improvement in speed &/or CPU usage
what-so-ever.

So my question: What am I doing wrong?

Any help in this area is/will be much appreciated. Especially since
there's not much documentation out there dealing with this stuff (IL
attribute tweaks, ActiveX interop, Zmodem & .NET,...was I set up to
fail or what!?!). Thanks.

- Larry
 
Just out of curiosity, what happens when you connect 8 clients? I'm
assuming CPU goes to max, but do the clients work?
-mike
MVP

Larry Nixon said:
I was tasked to build a .NET/C# server-side application that'd answer
calls from a modem bank & allow Zmodem uploads. I was given Sax's
SaxComm8 ActiveX component (already paid for & not much else out there
handles Zmodem; also, new Sax.NET framework STILL doesn't offer Zmodem
support yet).

With the help of VS.Net, I got the Interop & AxInterop DLL's generated
& working & thought all was well. That is, until I saw it under load.
Memory was cool, but CPU usage was at 50% with only 1 instance...&
that's WITH Hyperthreading (99% without)!

I then compiled the Sax component into an old Win32 app to test its
performance against .NET & it was a pig as well. But only half as bad
(w/Hyperthreading: 25%; w/o: 50%). So my next task was to attempt to
trim off as much of the 25% overage as was possible, seeing as I
needed to support 8 lines per server (& never a problem on our old
UNIX server running an ANSI C equilavent).

Anyway, after reading Mr. Nick Wienholt's recommendation for a few
other folks suffering similar performance problems here, I decided to
attempt to remove the run-time security checks/"stack walk" by using
the "SuppressUnmanagedCodeSecurityAttribute" attribute on the class
definitions &/or methods that are called frequently within the
generated Interop assemblies/"ActiveX wrappers" (which is supposed to
move the check into link/JIT time, which is supposed to pep things up
a bit).

Of course, this couldn't be straight forward. As Nick puts it: "A
painful exercise that would be trivial if the AxImp utility had an
'/unsafe' option &/or equivalent like the TlbImp utility does". So, I
had to disassemble these assemblies with the ILDASM utility, add the
attribute manually, & then reassemble the DLLs with the ILASM utility.
Here's how it went:

1.) Used ILDasm to generate MSIL source code for the assembly.
C:\Program Files\Microsoft Visual Studio .NET\FrameworkSDK\Bin>
ILDASM .\Interop.SaxComm8.dll /out=.\Interop.SaxComm8.il
ILDASM .\AxInterop.SaxComm8.dll /out=.\AxInterop.SaxComm8.il
This step also produced a RES file since resources were found in the
assembly (which I needed to include later).

2.) Modified the MSIL code with text editor in the "CLASS MEMBERS
DECLARATION" section by adding:
.custom instance void [mscorlib]
System.Security.SuppressUnmanagedCodeSecurityAttribute::.ctor() =
( 01 00 00 00 )
...in every ".class", ".method", ".event", & ".property" I could
(initially, just to see if performance was improved at all).

3.) Recompiled the MSIL using the ILASM compiler. Since a RES file
was generated, it was compiled back into the assembly during this step
using the /resource option on ILASM.
C:\WINNT\Microsoft.NET\Framework\v1.1.4322>
ILASM /DLL /resource=.\Interop.SaxComm8.res
/key=.\EfileReceiver.snk .\Interop.SaxComm8.il
ILASM /DLL /resource=.\AxInterop.SaxComm8.res
/key=.\EfileReceiver.snk .\AxInterop.SaxComm8.il

And...nothing. Bumpkiss. No improvement in speed &/or CPU usage
what-so-ever.

So my question: What am I doing wrong?

Any help in this area is/will be much appreciated. Especially since
there's not much documentation out there dealing with this stuff (IL
attribute tweaks, ActiveX interop, Zmodem & .NET,...was I set up to
fail or what!?!). Thanks.

- Larry
 
Larry,
I'm not really sure what's causing your performance problem (you don't have
any tight loops in your code do you? That could certainly cause the CPU to
jump to 100% without actually doing much work).

However, you mentioned you couldn't find a native C# library to provide you
with the comm routines and ZModem transfers. I did a quick search on google
and came up with this,
http://www.devdirect.com/All/elementscm_PROD_00007505.aspx, which appears to
be a managed C# library that fully supports comm communication and X/Y/Z
modem transfers. I don't know any more about it, and I've certainly never
used it, but it very well could be better than trying to COM interop. It
does seem a bit expensive to me, but hopefully that's not a problem.

Ryan Gregg




Larry Nixon said:
I was tasked to build a .NET/C# server-side application that'd answer
calls from a modem bank & allow Zmodem uploads. I was given Sax's
SaxComm8 ActiveX component (already paid for & not much else out there
handles Zmodem; also, new Sax.NET framework STILL doesn't offer Zmodem
support yet).

With the help of VS.Net, I got the Interop & AxInterop DLL's generated
& working & thought all was well. That is, until I saw it under load.
Memory was cool, but CPU usage was at 50% with only 1 instance...&
that's WITH Hyperthreading (99% without)!

I then compiled the Sax component into an old Win32 app to test its
performance against .NET & it was a pig as well. But only half as bad
(w/Hyperthreading: 25%; w/o: 50%). So my next task was to attempt to
trim off as much of the 25% overage as was possible, seeing as I
needed to support 8 lines per server (& never a problem on our old
UNIX server running an ANSI C equilavent).

Anyway, after reading Mr. Nick Wienholt's recommendation for a few
other folks suffering similar performance problems here, I decided to
attempt to remove the run-time security checks/"stack walk" by using
the "SuppressUnmanagedCodeSecurityAttribute" attribute on the class
definitions &/or methods that are called frequently within the
generated Interop assemblies/"ActiveX wrappers" (which is supposed to
move the check into link/JIT time, which is supposed to pep things up
a bit).

Of course, this couldn't be straight forward. As Nick puts it: "A
painful exercise that would be trivial if the AxImp utility had an
'/unsafe' option &/or equivalent like the TlbImp utility does". So, I
had to disassemble these assemblies with the ILDASM utility, add the
attribute manually, & then reassemble the DLLs with the ILASM utility.
Here's how it went:

1.) Used ILDasm to generate MSIL source code for the assembly.
C:\Program Files\Microsoft Visual Studio .NET\FrameworkSDK\Bin>
ILDASM .\Interop.SaxComm8.dll /out=.\Interop.SaxComm8.il
ILDASM .\AxInterop.SaxComm8.dll /out=.\AxInterop.SaxComm8.il
This step also produced a RES file since resources were found in the
assembly (which I needed to include later).

2.) Modified the MSIL code with text editor in the "CLASS MEMBERS
DECLARATION" section by adding:
.custom instance void [mscorlib]
System.Security.SuppressUnmanagedCodeSecurityAttribute::.ctor() =
( 01 00 00 00 )
...in every ".class", ".method", ".event", & ".property" I could
(initially, just to see if performance was improved at all).

3.) Recompiled the MSIL using the ILASM compiler. Since a RES file
was generated, it was compiled back into the assembly during this step
using the /resource option on ILASM.
C:\WINNT\Microsoft.NET\Framework\v1.1.4322>
ILASM /DLL /resource=.\Interop.SaxComm8.res
/key=.\EfileReceiver.snk .\Interop.SaxComm8.il
ILASM /DLL /resource=.\AxInterop.SaxComm8.res
/key=.\EfileReceiver.snk .\AxInterop.SaxComm8.il

And...nothing. Bumpkiss. No improvement in speed &/or CPU usage
what-so-ever.

So my question: What am I doing wrong?

Any help in this area is/will be much appreciated. Especially since
there's not much documentation out there dealing with this stuff (IL
attribute tweaks, ActiveX interop, Zmodem & .NET,...was I set up to
fail or what!?!). Thanks.

- Larry
 
Hey Bruno,

Thanks for the reply.

Yeah, I wasn't sure how much I should read into Task Manager's
CPU-Usage stat.

So if it's a single-CPU box (like my workstation/unit-testing
environment), 99% for a single instance is cool? (Most of my
co-workers with more Windows experience than me were gasping,
clutching their chests, & crying CPU bind.)

On our 2-way server with Hyperthreading, there are 8 instances (1 per
modem) continually running (daemon-like). When nobody's connected,
processes are idle (0%). When 1 line is connected, 50% usage overall.
2 active sessions = 99%. Above that: equal divisions of the pie.

Here's a better breakdown of our tests (w/2MB file, Zmodem, &
multi-CPU box):
Method (client-->host) Time (minutes) CPU-Usage
HyperTerminal----->HyperTerminal ~3 ~1-2%
HyperTerminal----->.NET/C#/Sax ActiveX ~11.5 ~50%
Sax Win32 Client-->Sax Win32 Host ~11 ~25%

....& the Sax Win32 apps are/were their VB6 examples (Who would/should
know better how to use their own component/product?). This download
speed also seemed to be dependent upon the CPU speed (faster processor
= quicker upload).

Question is: What is Hilgraeve (maker of "HyperTerminal") doing right
that me & Sax are doing wrong?

- Larry
 
Hey Michael,

Thanks for your response as well.

As far as your assumption (CPU going to max w/2+ concurrent
connections), you are correct, sir. Although, the most traffic we've
ever been able to test was up to 6 simultaneously. They all work, but
the downloads get painfully slow. For some reason, the file transfer
speed is tied directly to the processor speed (within this component).
I've tried increasing buffer sizes on the ActiveX component & cache
sizes on the server to no avail.

- Larry
 
Mr. Gregg,

Thanks for your response & help.

As far as performance problems dealing with any "tight loops in my
code", maybe... Since working with this ActiveX component, I'd
observed it spawning multiple threads when it does certain tasks,
requiring me to insert wait-states (in the form of a while loop) in
order to prevent a couple of race conditions I'd encountered. But in
my defense, this was derived from an example from Sax themselves.
Also, this CPU-usage percentage maintains consistently throughout a
session (when I know it isn't within any loops of my design). I'll
re-examine my code again, though, just in case.

Also, thanks for the tip on the library/framework:
http://www.devdirect.com/All/elementscm_PROD_00007505.aspx

Unfortunately, $$$ is always an issue, isn't it?

- Larry
 
(e-mail address removed) (Larry Nixon) wrote in
Mr. Gregg,

Thanks for your response & help.

As far as performance problems dealing with any "tight loops in my
code", maybe... Since working with this ActiveX component, I'd
observed it spawning multiple threads when it does certain tasks,
requiring me to insert wait-states (in the form of a while loop) in
order to prevent a couple of race conditions I'd encountered. But in
my defense, this was derived from an example from Sax themselves.
Also, this CPU-usage percentage maintains consistently throughout a
session (when I know it isn't within any loops of my design). I'll
re-examine my code again, though, just in case.

Also, thanks for the tip on the library/framework:
http://www.devdirect.com/All/elementscm_PROD_00007505.aspx

Unfortunately, $$$ is always an issue, isn't it?

- Larry


All I can say is, welcome to 3rd-party component hell.

I've worked with Dart and Infragistics components, and have been sorely
disappointed in both cases. Every day I hear of people having trouble
with their fancy and pricey components. I've pretty much decided that if
I want it done right, it will have to be done in-house.

Mark
 
Larry,

Take a look at CrystalComm
http://www.crystalcom.com/crs_cnt.htm


Larry Nixon said:
I was tasked to build a .NET/C# server-side application that'd answer
calls from a modem bank & allow Zmodem uploads. I was given Sax's
SaxComm8 ActiveX component (already paid for & not much else out there
handles Zmodem; also, new Sax.NET framework STILL doesn't offer Zmodem
support yet).

With the help of VS.Net, I got the Interop & AxInterop DLL's generated
& working & thought all was well. That is, until I saw it under load.
Memory was cool, but CPU usage was at 50% with only 1 instance...&
that's WITH Hyperthreading (99% without)!

I then compiled the Sax component into an old Win32 app to test its
performance against .NET & it was a pig as well. But only half as bad
(w/Hyperthreading: 25%; w/o: 50%). So my next task was to attempt to
trim off as much of the 25% overage as was possible, seeing as I
needed to support 8 lines per server (& never a problem on our old
UNIX server running an ANSI C equilavent).

Anyway, after reading Mr. Nick Wienholt's recommendation for a few
other folks suffering similar performance problems here, I decided to
attempt to remove the run-time security checks/"stack walk" by using
the "SuppressUnmanagedCodeSecurityAttribute" attribute on the class
definitions &/or methods that are called frequently within the
generated Interop assemblies/"ActiveX wrappers" (which is supposed to
move the check into link/JIT time, which is supposed to pep things up
a bit).

Of course, this couldn't be straight forward. As Nick puts it: "A
painful exercise that would be trivial if the AxImp utility had an
'/unsafe' option &/or equivalent like the TlbImp utility does". So, I
had to disassemble these assemblies with the ILDASM utility, add the
attribute manually, & then reassemble the DLLs with the ILASM utility.
Here's how it went:

1.) Used ILDasm to generate MSIL source code for the assembly.
C:\Program Files\Microsoft Visual Studio .NET\FrameworkSDK\Bin>
ILDASM .\Interop.SaxComm8.dll /out=.\Interop.SaxComm8.il
ILDASM .\AxInterop.SaxComm8.dll /out=.\AxInterop.SaxComm8.il
This step also produced a RES file since resources were found in the
assembly (which I needed to include later).

2.) Modified the MSIL code with text editor in the "CLASS MEMBERS
DECLARATION" section by adding:
.custom instance void [mscorlib]
System.Security.SuppressUnmanagedCodeSecurityAttribute::.ctor() =
( 01 00 00 00 )
...in every ".class", ".method", ".event", & ".property" I could
(initially, just to see if performance was improved at all).

3.) Recompiled the MSIL using the ILASM compiler. Since a RES file
was generated, it was compiled back into the assembly during this step
using the /resource option on ILASM.
C:\WINNT\Microsoft.NET\Framework\v1.1.4322>
ILASM /DLL /resource=.\Interop.SaxComm8.res
/key=.\EfileReceiver.snk .\Interop.SaxComm8.il
ILASM /DLL /resource=.\AxInterop.SaxComm8.res
/key=.\EfileReceiver.snk .\AxInterop.SaxComm8.il

And...nothing. Bumpkiss. No improvement in speed &/or CPU usage
what-so-ever.

So my question: What am I doing wrong?

Any help in this area is/will be much appreciated. Especially since
there's not much documentation out there dealing with this stuff (IL
attribute tweaks, ActiveX interop, Zmodem & .NET,...was I set up to
fail or what!?!). Thanks.

- Larry
 
Sax is completely useless. When I'd contacted (filled out an
incidence report; no human contact) them in the past, they'd simply
stated that their component wasn't supported in the .NET environment.
I tried again for this & got nothing. Thanks Sax [sarcastically].
 
Thanks for the link to CrystalComm, Darin. But in the meantime, I
think I found a temporary solution (by way of Mr. Ryan Gregg's
suggestion to check for 'tight loops'; thanks again):

// to prevent a race condition until file transfers are complete
// (multi-threaded ActiveX component)
while (_axSaxComm.XferStatus > 0) {
System.Windows.Forms.Application.DoEvents();
...}

You'd think that DoEvents (a new call for me, but suggested by Sax
themselves in their own examples & help) would provide something for
the loop to do (& not just let it spin upon itself uncontrollably
eating CPU cycles). I also have a few 'if' conditions in there, but
'if' they're not called...

I'd always been taught that sleep's were kinda hoakey if you needed
them for your stuff to work right. I just tried it on a lark with a
token 1 millisecond sleep & whah-lah! An 8 1/4 minute 2MB upload
magically transforms into a 3 1/2 minute one (on par with a
HyperTerminal-->HyperTerminal transfer; fastest I've encountered).

So, before I put this baby to bed, am I doing anything blatently
wrong? Dangerous? Just plain stupid? Or, is this normal fare for
'DoEvents' to do less work than a 1 millisecond sleep (& cause serious
CPU performance issues)?

- Larry
 
Back
Top