Logo




Subscribe:
RSS 2.0 | Atom 1.0
Categories:

Sign In


[Giagnocavo]Michael::Write()

 Wednesday, March 16, 2005
SOAP Performance (gSOAP / ASP.NET)

I'm doing my own realtime support for Asterisk, in an attempt to make it scale. Asterisk is nice software, but straight out-of-CVS, the performance for high volume (say, over 20,000 clients) sucks. There are also other inconviences with using a file-based store to determine how to route calls. Mainly, it's inflexible and hard to achieve high-perf when everything is based on a large .conf file. Not to mention that Asterisk uses linked lists for everything so looking up any user is an O(N) op (and parsing the users file is O(N*N) by default!). So, I'm going to put my own logic as a replacement for some of the critical parts.

One of my concerns was performance. Since I'll have multiple Asterisk clusters banging on my .NET code (via SOAP), I wanted to ensure the whole end-to-end process was fast enough.

I used gSOAP to create the C code on Linux. gSOAP is seriously nice. At least an order of magnitude easier to use than I expected any SOAP library that works on Linux would be.

I created a simple test. I made a database with phone numbers and codecs. The idea is that when an incoming call comes in, Asterisk will use my code to SOAP over to my Windows machines, get the data, and then go on its merry way.

My Asterisk machine is a P4 2.4GHz, 512MB RAM (but, I have a Gnome session running on it). My Windows XP machine (I tested against my desktop) is a P4 3GHz HT, 1.5GB RAM. I'm running ASP.NET 2 Beta 1 and SQL Server 2000.

The test program consists of a loop (count 5000) that generates a random number, then uses gSOAP to ask for the codec for that number. Simple, tight.

The results on Linux are particularly impressive. Each instance of the test app only used a max of 4% processor, and under 1MB of RAM. The bottleneck was definately inside ASP.NET. To simulate more load from other machines in a cluster, I ran 1, 2, and 4 instances of the test program. Also note that background tasks on the XP machine used up about 10% of the CPU.

Results:
 Single process (5000 total requests): 
  Total time:                   18 seconds (0.0036s/request)
  Requests per second:   277
  ASP.NET/IIS CPU:          30%
  SQL Server CPU:            4%

 Dual process (10,000 total requests):
  Total time:                   23 seconds (0.0048s/request)
  Requests per second:   384
  ASP.NET/IIS CPU:          60%
  SQL Server CPU:            7%

 Quad process (20,000 total requests):
  Total time:                   42 seconds (0.0052s/request)
  Requests per second:   476
  ASP.NET/IIS CPU:          80%
  SQL Server CPU:           10%

These results are encouraging enough that I'm not worried of the performance impact of using SOAP with Asterisk. My target was to have a response in less than 0.1 seconds. Although, anything under 0.5s would be quite unnoticable to a client. Even in tests with more threads, my single request response time was always way under 0.1 seconds.

Also, as far as I know, Whidbey Beta 2 (the version I'll go live with) makes some performance improvements. And also, IIS6 on Windows 2003 is much faster than IIS5.1 on XP. At any rate, a single proc desktop machine serving 476 RPS? That's pretty damn good perf if you ask me!

ast_mono | Asterisk | Code
Wednesday, March 16, 2005 5:06:55 PM UTC  #    Comments [1]  |  Trackback

 Wednesday, November 24, 2004
ast_mono - First class compiles!

I chose to expose cli.h, since it's very simple :). My code generator is now debugged enough to generate all the things required for the ManagedAsterisk and ast_mono runtime to compile and allow access to a specific .h. Even the simple cli.h, which is only 92 lines long, requires about 500 lines of output (C and C#) to be fully wiredup (of course, this counts spacing and open/close braces).  

Anyways, a bit of tweaking the generated code (and applying the fixes to the generator), it now compiles just fine without any warnings or errors. Yey.

ast_mono
Wednesday, November 24, 2004 7:18:18 AM UTC  #    Comments [0]  |  Trackback

 Sunday, November 21, 2004
ast_mono - Phase 02 - Classes and Libraries

As mentioned before, one of the challenges of ast_mono is creating a “.NET-like” library, when the underlying code is all C. Since there's no real grouping in the C API other than which functions are in a certain header file, I don't have much to go on. Here's a sample of the current thinking for the managed API:

ManagedAsterisk.Core.Cli
  Contains the cli.h constants implemented as public static readonly fields. The value is loaded in the .cctor via an internalcall that returns the constant. This was done so that if a C constant is changed later on, recompiling the ast_mono runtime will provide the new value to managed code, without any changes.
  Also contains “static” functions, such as ast_cli_command, however, renamed to Command (ManagedAsterisk.Core.Cli.Command).

ManagedAsterisk.Core.Cli.Native
  This contains all the internalcall declarations to call the runtime wrapper methods. For now, access is public to all the functions, such as ast_cli_command, ast_cli_register and so on. Field accessors (see next) and constant accessors are not accessible and must be accessed thru the respective classes. I'm not completely sure on making this public, and might mark it internal instead (unless there's a good reason that it needs to be public).

ManagedAsterisk.Core.CliEntry
  This class is the managed version of struct ast_cli_entry. All the data is kept in unmanaged memory, and only a pointer is kept in managed memory, using internalcalls to access fields. It's constructed via new (heap allocated), or it's constructed by passing a pointer. It has a manual free (IDisposable) method, but it's *not* finalizable. This is because it's quite possible that the managed reference will go out of scope while on the unmanaged side it might be still in use. Automatic finalization (And hence a free) of it could easily cause Asterisk to segfault. Do note that improper usage could lead to a memory leak, although in most cases.

  The fields, (char* usage, struct ast_cli_entry *next;, int inuse) are all available as standard class properties. They are renamed to reflect that (public string Usage{ get; set; }, etc.). Methods that take a struct ast_cli_entry* will take a CliEntry class instead.

ManagedAsterisk.Core.CliEntry.Native
  Private implementation details to get/set the data (something like ast_mono_wrapper_ast_cli_entry_set_usage). Internalcalls set/get values, as well as enforce data limits (such as array bounds -- no need to worry about buffer overflows). No plans to make publically exposed.

---
For files like channel.h, where there is a lot of both static and “instance” methods, things will be put into one class, Core.Channel. There won't be a separate Core.ChannelMethods or Core.ChannelClass. The cli.h just happened to lend itself to this format.

Currently, the ast_mono code generator is in the final stage of taking the SWiG-generated XML and producing the various declarations and wireup code required to make everything work. This generated code will be heavily hand-edited before becoming part of the ast_mono/ManagedAsterisk platform.

ast_mono
Sunday, November 21, 2004 6:05:12 AM UTC  #    Comments [0]  |  Trackback

 Sunday, November 14, 2004
ast_mono: printf functions

I was working on ast_mono tonight, and had to make some decisions on how the ManagedAsterisk API would handle formatted (printf style) functions.

First, there's technical issues. printf functions use varargs. I'm not exactly sure on how to forward and prototype these methods. I know IL supports varargs, but I don't think that C# does. So, I'd end up doing a “params object[] args” parameter. The issue is that my unmanaged runtime is then massed a set of pointers, and would have to figure out what each item is to be able to format it correctly. Still, it's possible.

Then the real issue comes into play. .NET APIs don't use printf style functions. To many .NET programmers this style would seem foreign, and clash with other APIs. Since one of the goals of ast_mono is to present an API that's consistent and feels like any other .NET library, this is unacceptable. So, instead, I'm doing to use string.Format to handle the formatting for these functions (such as ast_log). Yes, the functionality is somewhat different, but overall it should provide a much better development experience to ast_mono users.

ast_mono
Sunday, November 14, 2004 4:45:08 AM UTC  #    Comments [0]  |  Trackback

 Thursday, November 04, 2004
GCC Visual Studio Integration

As I mentioned a few posts ago, I have a makefile project in MSVC++ 8 (2005) setup. Part of my solution builds with GCC on Linux, part on Windows with CSC. By using plink (command-line version of Putty), I'm able to ssh over to my Linux machine and build. The errors show up in the error list in VS2005. Except for one slight problem: Visual Studio does not read the error line information correctly, resulting in an error if I try to click and goto that line.

GCC outputs errors so: my.c:123: error: you suck. VS expects them as my.c(1) : error you suck. So, I decided to write a filter for the output. But, since it was around 2 AM, I decided to check and see if GCC supports out in the style that VS expects. As far as I can tell, it doesn't. But, I found a link to a page that has a program that accomplishes just this (GNU2MSDEV): http://www.xs4all.nl/~borkhuis/vxworks/vxw_pt1.html#1.13

Yey, I'm done, right? Well, not quite. GNU2MSDEV reads from stdin. GCC puts the errors on stderr. So, the parser only gets part of the output, missing the critical parts. Ouch. Now, since it's past my bedtime, I spent a while trying to figure out what was going on, thinking perhaps the program was broken. Got the source, uncommented some debugging stuff, and finally, I realised that I'd need to get stderr send over stdin, as the easiest course of action.

I'm not sure how you redirect stderr in the NT command shell. One page I read said this was impossible from the command line, and you need to use a different shell (they suggested running Linux), or write a program to do it for you. I decided to just add this to my linux commands. Now, my VS build command line looks like this:

plink 192.168.0.123 -ssh -l myUser -pw myPassword "cd /usr/src/something;make clean; make install 2>&1;" | gnu2msdev

Presto. Now it works just perfectly, and I can double click the error list and go right to the file/line where the error is. Of course, now it's 3AM for some reason, and instead of writing some code to record phone calls, I'm instead going to go to sleep.

ast_mono | Code
Thursday, November 04, 2004 9:02:41 AM UTC  #    Comments [0]  |  Trackback

 Tuesday, October 26, 2004
Ok, maybe I'll take a small SWiG

As I was designing exactly how I was going to generate all the C# prototypes and basic C implementations for my ast_mono internalcalls, I realised it'd be a bit more work than I feel like doing, especially where there's already work done. I'm not looking for a completely automated way to create code files that can be updated on-the-fly. I think there will be a lot of customization going into each generated file, and I'm comfortable with that.

So what I really want is a simple way to have some of my C# code emit some definitions and code into two files (the C# prototype file and the C internalcall file). I don't really care to actually process C header files all -- all I want is access to the type definitions, even in a limited form. In taking a deeper look at SWiG, I found that, lo-and-behold, it also emits its parse trees as XML if I so desire. Sweet. Now I'll have a nicely packaged way to get at the type info of all the header files for Asterisk.

ast_mono
Tuesday, October 26, 2004 7:20:26 AM UTC  #    Comments [0]  |  Trackback

ast_mono - Phase 01 - Structures

Asterisk depends on quite a few structures, obviously :). One of my goals for ast_mono is to make the API seem as .NET-friendly and standard as possible. This includes creating a real object oriented API, rather than the C way (where you pass the structure in as the first parameter). At runtime it'll be nearly the same thing, but it really changes the whole of the API.

Before I can do any real function calls or library work, I need to have the base classes defined: channels, files, etc. My first approach was the “nicest”. My idea was to prototype the structures in C#, allowing direct access from the .NET runtime to the structure, using C# unsafe code (i.e., ast_channel *myPtr; myPtr->language). I'd rolle the unsafe code inside the class, so to an end-user, it'd appear as a normal property.

However, I don't trust that all the structures in Asterisk will stay the same all the time. If a field was added or moved around, I'd have to declare a whole different prototype. To make things worse, I think it's entirely possible that there could be some #defines that people change per-platform, and that there could be multiple valid prototypes that I'd need to support (as part of the C# build process). My goal is to ship a runtime that can be built on the target machine, but the C# API should be able to be distributed in binary form without any issues. One main goal of this is that building ast_mono won't require a C# compiler. The IL binaries should be fully portable by themselves.

So, despite the advantages and elegancy of being able to access the structures with pure managed code, it's not going to happen. I checked out SWiG, and it's quite powerful. SWiG allows you to take C/C++ headers and turn them into definitions for C#, Java, Perl, and a slew of others. I might possibly be able to use this tool as a basis for my app.

For C#, SWiG creates a class that takes a pointer to the unmanaged structure. Then it generates properties in C# that call P/Invoke methods on a C class it generates. So to access the language field of ast_channel, the get property accessor P/Invokes to ast_channel_get_language. This is OK. However, I believe it can be done better using internalcalls.

Mono allows a runtime host to register internalcalls. Internalcalls run as part of the runtime, and have access to the native .NET types. This removes the need for marshalling to those calls: a big plus. Obviously there will still be some marshalling needed for some types (i.e. taking a null-terminated ANSI string and creating a .NET string), but it'll be easier to work with. For the performance and flexibility of doing things this way, I think it's worth the bit of extra work to write a small program to generate some of the wrapper code for me instead of using SWiG. (I also am not very fond of the code generated by SWiG.) Also, I should be able to grab the comments from the headers and generate some basic XML docs.

Methods are quite a bit simpler, with the same basic principles applying. I'll extend my code to emit some basic definitions (C# prototypes + basic C Mono implementations) and that should get me going.

On a side note, although right now I'm sticking to the ISO spec of C#, Mono 1.2 has support for many of Whidbey's features. This means I get to use generics soon. Also, I am going to be compiling with the Whidbey C# compiler, so compile-time only features, such as partial classes, are available too. Yum :).

ast_mono
Tuesday, October 26, 2004 3:50:43 AM UTC  #    Comments [0]  |  Trackback

 Sunday, October 17, 2004
ast_mono_loader -- Embedding Mono into Asterisk - Phase 00

Asterisk (www.asterisk.org), is a piece of extremely powerful VoIP/PBX software. It can bridge just about any codec (u/aLaw, G.729, G.xxx, GSM) and protocol (SIP, SCCP, H323, and it's own IAX). Plus it's got a ton of cool features and is extensible. The only downside is that for maximum power, you must use the C API. For some people, this means low productivity. For others, it means forgetting about having great flexibility and power.

I like C#, and I want to write most of my code in C#. But, I'm also going to be writing a lot of code for Asterisk. So, I'm doing the only sensible thing: Embed Mono (www.go-mono.com) into Asterisk and load my .NET assemblies from there, while exposing the complete Asterisk C API to my .NET applications. (In other words, much more than just spawning a new process and passing args and reading stdout).

Well, after spending 2 days trying to get Mono to work and my code to link to it, I finally got things working (without formatting and laying down Fedora Core 2!). Nothing much yet, but I got a sample program to be loaded into the Asterisk process, and I executed by dialing an extension. Now I just need to complete the API, enhance the loader, and do everything else, and I'll have a very solid project.

If you use Asterisk and would like to write with C#, contact me (mgg @ this domain .com). I'm also in #asterisk on irc.freenode.org as AgiNamu.

Or, if you like Microsoft and .NET, but want to get into VoIP, drop me a line. I haven't seen any real solutions that compete against Asterisk for Win32 (or Win64).

ast_mono
Sunday, October 17, 2004 12:41:52 AM UTC  #    Comments [1]  |  Trackback