|
The Sun ejecting matter from its surface.
A bit like me trying to write C under time
pressure!
Image from NASA. |
Too Long to Write Complex C
Over the last few days, I have been coding using the JVMTI. The goal is to accurately time a JNI interface layer. I could not even contemplate using C even though it is the preferred language for the project. Not because C is too hard to code in, or lacks some critical feature, it just takes too long to write complex C.
Don't get me wrong - I love C for some stuff. For writing really detailed super fast code, it can be just the ticket. It is not that one cannot do this in C++, just that C feels more suited to it. However, C takes far too long to code up when complex data marshalling or logic gets involved. Also, the very act of writing it is so slow because of its challenges to the IDE. C's type system feels like - and actually is - an after thought. This means that IDEs struggle to give much help as to how to auto-fill and error check. No inheritance makes encapsulating logic and separating concerns very coding heavy. Yes - one can use structures of function pointers and they work beautifully; however, all that key tapping and manually checking everything is set up correctly just takes time and is very brittle when code is being actively developed or maintained.
Then there is memory management. malloc
/free
are pains to work with. With unique_ptr
, shared_ptr
and RAII, C++ is thousands of times easier to work with. The only possible exception to this is exception handling. Nevertheless, following simple rules ensures that exception safe code can be written.
Here is an example - somewhat trivial - that kind of illustrates my point:
class NativeEntryPayload{
static CounterType stopStartOverhead;
void *nativeEntryPoint;
JVMTimer nativeClockCycles;
JVMTimer jvmClockCycles;
string currentJVMBlock;
CounterType jvmBlockStartTime;
map<string,pair<CounterType,long>> jvmBlockTimes;
public:
NativeEntryPayload(void *entryPoint) :
nativeEntryPoint(entryPoint)
{}
void *getNativeEntry(){ return nativeEntryPoint; }
void enterNativeBlock(){
nativeClockCycles.startTheClock();
}
void leaveNativeBlock(){
nativeClockCycles.stopTheClock();
}
void enterJvmBlock(const string const &callBackName){
currentJVMBlock=callBackName;
jvmBlockStartTime=jvmClockCycles.startTheClock();
}
void leaveJvmBlock(){
jvmClockCycles.stopTheClock();
auto x=jvmBlockTimes[currentJVMBlock];
x.first +=jvmClockCycles.getTheClock();
++x.second;
}
pair<long long,CounterType> getNativeClockCycles(){
return pair<long long,CounterType>(nativeClockCycles.getNCounts(),
nativeClockCycles.getTheClock());
}
pair<long long,CounterType> getJVMClockCycles(){
return pair<long long,CounterType>(jvmClockCycles.getNCounts(),
jvmClockCycles.getTheClock());
}
static void setStopStartOverhead(CounterType value){
stopStartOverhead=value;
}
static CounterType getStopStartOverhead(){
return stopStartOverhead;
}
};
This class keeps track of the number of clock cycles consumed by native code in a Java/JNI application and along side that the number of cycles take by each call back into the JVM. This would be a very slow to code up in C due to the heavy tracking of data which is all of dynamic size. I have nothing against writing this sort of thing in C, as long as development time is not an issue. But, in the real world. Let's take just a couple of lines:
void leaveJvmBlock(){
jvmClockCycles.stopTheClock();
auto x=jvmBlockTimes[currentJVMBlock];
x.first +=jvmClockCycles.getTheClock();
++x.second;
}
To implement this in C, I would have to make a structure for the pair
used to track the time and number of times called for each JVM method. I would have to use function calls to move information in and out of the map (which is actually a tree under the covers) rather than the natty []
syntax. I would have to explicitly type the variable x
rather than use auto. Further, Visual Studio would not just work out all the types and let me use auto complete everywhere. In general - just this bit of code would take - as a guess - 3 to 4 times longer to write in C. That is no accounting for the fact that the whole thing would need to be written as a set of functions taking a structure as one of the arguments. Further, there could be no separation between public
and private
members and the code would be nowhere near as self documenting so I would have to fill it with comments and then ensure they stayed up to date as I hacked on the code.
Really - life is too short. If you can use C++ rather than C - do so!