views:

139

answers:

3

Hi all.

I'm currently working on a performance critical application which incorporates legacy c code (a SPICE variant).

The problem is as follows:

The creators of the legacy c code apparently believed that the use of argument passing is one of the great evils of the modern age. Thus about 90% of all the variables were declared globally.

The legacy code has to be called repeatedly for simulation purposes and it would be seam that using threads for concurrent calls to this code section would be beneficial for the overall execution time.

My idea was to encapsulate the legacy c code (which I already modified slightly for g++ compilation) so that multiple objects for the legacy code can be created removing the necessity of to many mutex locks. Obviously the global variables will thus be encapsulated as member variables.

This brought another problem to the table. The legacy coders also did not believe in the initialization of global variables; probably since C tend to init global variables to 0. C++ member variables does not seam to get the same treatment though. A few variables has to be initialized to 0 for the correct operation of the legacy c code. But finding these variables have proven to be quite difficult due to the vast amount of global variables used.

Please keep in mind that time does not permit me to modify the legacy c code to any major degree.

My questions are as follows:

  1. Am I correct in the assumption that the encapsulation of the C code would be faster than the use of about 90 mutex locks?

  2. Is there an easy way of finding uninitialized member variable use? (As I understand gcc can only do this for automatic variables). So that I can init only the critical variables?

  3. If not Question 2 ... is there a fast and clean way to initialize all the member variables without using the normal constructor init method?

  4. Or (a long shot) is there a fast way of following program flow so that I might incorporate the argument passing myself?

Any help will be vastly appreciated. N.B.

+3  A: 
  1. Yes. If you can put the state into objects to which you pass pointers around, it will be faster than locking, assuming you actually do use threads.
  2. No, it's not easy to find out unitialized member variables. Essentially, that would require to perform whole-code analysis, which it typically can't do (due to the existence of libraries)
  3. If you put all data in an old-style struct (i.e. no methods, no access declarations), you are allowed to memset() the entire struct to zero. This will cause initialization in the same way as global variables get initialized (which C does guarantee to initialize - to zero).
  4. If, by fast, you mean "automatic", then the answer is probably "no".
Martin v. Löwis
WRT the memset, you can get away with this so long as you don't have a virtual table. That is, you can have member functions so long as they aren't virtual, and you can have inheritance so long as it too isn't virtual. My advice - wrap the old C variables in a POD struct, and put that inside a class. memset the struct in the class constructor, but add any new member variables to the class directly (not the struct).
Steve314
Steve: WRT the memset, the easiest way is to put them into a struct of their own, and use it as base class.
peterchen
@Steve: this is not strictly correct. If you have members in a struct which have a non-trivial constructor, you shouldn't initialize the struct with memset, either.
Martin v. Löwis
You could make a POD struct to hold the variables and a class containing the struct and the function as methods. Then you can initialize the variables using memset but you can still use virtual functions and more in your class. But you have to rewrite the access to the variables by adding the member containing the structure.
rstevens
A: 

Have you thought about just using fork rather than making an oo-spice? It would seem to require very little changes to the code, and unless you're trying to do lots of analyses of very small models might not be too costly in terms of performance.

Pete Kirkham
I am indeed doing a lot of analysis on relatively small circuits. So I thought that the overhead of using processes for every spice call would be substantial.
Neville Bamshoe
On UNIX and Linux, creating a new process with `fork` is quite cheap. All code is already in memory, and memory can be marked as "copy on write".
MSalters
+1  A: 

It you are going to use multiple threads, you either need to eliminate or lock data shared amongst the threads. Making old, non-thread safe code reentrant is going to mean a lot of work and a lot of debugging. If you don't have a lot of time to dig in to the legacy code, I wouldn't try. You are opening a huge can of worms.

  • You can ensure initial 0 values for your globals in g++ with -fno-common. See the gcc manual.

  • I think you are much better off pursuing the suggestion of using multiple processes rather than multiple threads. Each process has its own addresses space and you won't have to touch anything in the legacy code. I would look very hard for a way to find a way to decompose you problem at a process level. (eg. don't do it for every call, do it big for logically independent chunks of the problem: like batches of 1000 circuits at a time.)

  • Before optimizing, really look very hard at why it's slow in the first place. Making existing serial code parallel is very hard work for potentially limited payoff. Are you sure that's what you want to do.

Regards, Matt

Matt