For performance, write it in C

D

Doug H

Peter said:
If you really really want that performance boost then take the following
advice very seriously - "Write it in C".

Assuming you have no choice but C/C++. That's why I like using the
jvm or clr with languages like jruby, groovy, or boo. You don't have
to use C, you can use java or C# or boo itself (since it is statically
typed with type inference): http://boo.codehaus.org/
or C/C++ as well, although it is 100x easier to interface with a C lib
from the clr than it is from jvm with jni.
 
K

Kristof Bastiaensen

Whenever the question of performance comes up with scripting languages
such as Ruby, Perl or Python there will be people whose response can be
summarised as "Write it in C". I am one such person. Some people take
offence at this and label us trolls or heretics of the true programming
language (take your pick).

<snip>

Hi,

When reading your C code, I saw that there is a lot of code that is
generated. I'd be interested to see how well the C program does if
it can work for any size of the squares. In this case I think the problem
is well suited for logic languages. I wrote a version in the functional
logic language Curry, which does reasonably well. It will probably not be
faster than the C version, but a lot faster than a program written in
Ruby/Perl/Python.
If you really really want that performance boost then take the following
advice very seriously - "Write it in C".

It can be a good idea to rewrite parts in C, but I would first check if
the algorithms are good, so that it may not even be needed to write any
C code. And perhaps there are libraries or tools that do the trick
efficiently. I would keep writing C code as the last option.

Regards,
Kristof

-------------------- start of latin.curry ----------------------------
-- upto is a nondeterministic function that evaluates to
-- a number from 1 upto n
upto 1 = 1
upto n | n > 1 = n ? upto (n-1)

-- check if the lists r s have no element with the same value at the
-- same position
elems_diff r s = and $ zipWith (/=) r s

-- extend takes a list of columns, and extends each column with a
-- number for the next row. It checks the number agains the column and
-- against the previous numbers in the row.

extend :: [[Int]] -> Int -> [[Int]]
extend cols n = addnum cols [] where
addnum [] _ = []
addnum (col:cs) prev
| x =:= upto n &
(x `elem` prev) =:= False &
(x `elem` col) =:= False = (x:col) : addnum cs (x:prev)
where x free

latin_square n = latin_square_ n
where latin_square_ 0 = replicate n [] -- initalize columns to nil
latin_square_ m | m > 0 = extend (latin_square_ (m-1)) n

square2str s = unlines $ map format_col s
where format_col col = unwords $ map show col

main = mapIO_ (putStrLn . square2str) (findall (\s -> s =:= latin_square 5))
------------------------- end latin.curry -----------------------------
 
V

vasudevram

Kristof said:
Hi,

When reading your C code, I saw that there is a lot of code that is
generated. I'd be interested to see how well the C program does if
it can work for any size of the squares. In this case I think the problem
is well suited for logic languages. I wrote a version in the functional
logic language Curry, which does reasonably well. It will probably not be

Interesting ... I read somewhere that the OCaml language, while
higher-level than C (and a functional one too), runs some programs at
least, as fast or faster than C ...
Not sure how true that is ...

Vasudev
http://www.dancingbison.com
 
S

Sean O'Halpin

In theory if two programs in two different languages produce the same
exact results the perfect compilers for each of the languages would
end up producing the same code. In theory practice is the same as
theory but in practice it isn't.

Cheers,

Pedro.
In theory, an infinite number of computer scientists hacking for an
infinite amount of time on a keyboard will eventually almost surely
produce a perfect compiler.

In practice, I can't wait that long ;)

Cheers,
Sean
 
D

David Pollak

Writing code that runs as fast in Java as it does in C is real work,
but it's possible.

Integer (http://athena.com) is a pure Java spreadsheet. I optimized
the numeric functions and array functions (e.g., SUM(A1:G99)) such
that Integer runs as fast or faster than Excel and OpenOffice Calc on
identical hardware. However, it required a mind shift from "standard"
Java programming.

In addition, because Java has nice semantics for multithreading, I was
able to implement some very cleaver algorithms such that Integer's
recalculation speed scales nearly linearly with additional CPUs up to
a certain point (the linearity goes away at around 16 processors on a
big Sun box.) But I digress.

First, I pre-allocated a lot of workspace so that there's little
memory allocation going on during recalculation.

Second, I avoid Monitors (synchronized) as much as possible.

Third, I write "C" style Java (lots of switch statements, putting
parameters and results in buffers rather than passing them on the
stack, etc.)

Memory usage in Java is higher than in C. If Java has Structs a la
C#/Mono, it'd be possible to squeeze that much more performance from
Java.

There are some applications that will never perform as in Java (e.g.,
stuff that's heavily oriented to bit manipulation.) But for many
classes of applications (e.g., spreadsheets) Java can perform as well
as C.

When I care about computational performance, I go with Java or in a
rare case, C++ (C style C++, no STL or virtual methods). If I care
about developer performance, I've been going with Ruby more and more.

My 2 cents.
 
G

Gregory Brown

In addition, because Java has nice semantics for multithreading, I was
able to implement some very cleaver algorithms such that Integer's
recalculation speed scales nearly linearly with additional CPUs up to
a certain point (the linearity goes away at around 16 processors on a
big Sun box.) But I digress.

This must be evidence of true cutting edge development ;)
 
I

Isaac Gouy

Tomasz said:
Sorry, I just couldn't resist - but maybe you should code Java instead -
http://kano.net/javabench/ ;-)

"The results I got were that Java is significantly faster than
optimized C++ in many cases... I've been accused of biasing the results
by using the -O2 option for GCC..."

"...so I took the benchmark code for C++ and Java from the now outdated
Great Computer Language Shootout and ran the tests myself"

Not so outdated
http://shootout.alioth.debian.org/gp4sandbox/benchmark.php?test=all&lang=java&lang2=gpp
 
M

Martin Ellis

Sean said:
In theory, an infinite number of computer scientists hacking for an
infinite amount of time on a keyboard will eventually almost surely
produce a perfect compiler.

In practice, I can't wait that long ;)

You'd be waiting a long time indeed :eek:).

I believe our good friend Mr. Turing proved [1] that such
a compiler could never exist, some seven decades ago.

Oh well.

Martin


[1] OK. That wasn't exactly what he proved.
But only this particular corollary is relevant.
 
D

David Pollak

Greg,

In spreadsheets, it is cutting edge. Name one other commercial
spreadsheet that can use more than 1 CPU?

David
 
J

James Edward Gray II

Greg,

In spreadsheets, it is cutting edge. Name one other commercial
spreadsheet that can use more than 1 CPU?

I'm pretty sure Greg was funning around with the comical typo in your
post. Take a look back at how you spelled "clever." ;)

James Edward Gray II
 
A

ara.t.howard

Whenever the question of performance comes up with scripting languages such
as Ruby, Perl or Python there will be people whose response can be
summarised as "Write it in C". I am one such person. Some people take
offence at this and label us trolls or heretics of the true programming
language (take your pick).

I am assuming here that when people talk about performance they really mean
speed. Some will disagree but this is what I am talking about.

In this post I want to clear some things up and provide benchmarks as to why
you should take "Write it in C" seriously. Personally I like to write my
projects in Ruby or Perl or Python and then convert them to C to get a
performance boost. How much of a boost? Well here I will give you some hard
data and source code so that you can see just how much of a boost C can give
you.

The mini project in question is to generate all the valid Latin squares. A
Latin square is a grid of numbers (lets say a 9 x 9 grid) that has the
numbers 1 to 9 appear only once in each row and column. Sudoku grids are a
subset of Latin squares.

The approach taken is to create a list of all the permutations and then
build up a grid row by row checking that the newly added row does not
conflict with any of the previous rows. If the final row can be added
without problem the solution is printed and the search for the next one
starts. It is in essence depth first search. The first version of the
program that I wrote in Perl took 473 minutes to generate all the valid 5 x
5 Latin squares, version four of the program took 12 minutes and 51 seconds.
The C version of the program took 5.5 seconds to produce identical results.
All run on the same hardware.

just for fun, here's a ruby version (note that the array would actually need
to be reformed into rows, but someone else can play with that)

harp:~ > cat a.rb
require 'gsl'

n = Integer(ARGV.shift || 2)

width, height = n, n

perm = GSL::permutation.alloc width * height

p perm.to_a until perm.next == GSL::FAILURE


it's not terribly fast to run - but it was to write!

-a
 
G

Gregory Brown

I'm pretty sure Greg was funning around with the comical typo in your
post. Take a look back at how you spelled "clever." ;)

James gets right to the point. I was just taking a slice at your
typo, not Integer. :)
 
C

Chad Perrin

I dont doubt for simple applications and algorithms java is nearly as
fast as C if not equivalent. Though for larger java projects such as
Eclipse, i've had a horrible time of it being slow and cumbersome on the
system, and Visual Studio will run fine and be far more responsive.
I dont really know why that is it could be as simple as some bad code in
the java gui layer that Eclipse is using.

Doubtful. Java does generally produce notably faster applications than
Ruby, and there are benchmarks that show that in specific instances it
can hustle almost as well as C -- even scalably so. A more
comprehensive survey of benchmarks, on the other hand, starts to take
its toll on Java's reputation for speed. The first problem is that C
isn't object oriented and, while OOP can be great for programmer
productivity under many circumstances (particularly involving larger
projects), it introduces a bunch of interface activity between parts of
the program which begins to slow things down. Furthermore, Java's
bytecode-compilation and VM interpretation can increase execution speed
at runtime by cutting out part of the process of getting from source to
binary, but it still requires interpretation and relies on the
performance of the VM itself (which is, sad to say, not as light on its
feet as many would like).

In fact, there are cases where the Perl runtime compiler's quickness
makes Java's VM look dog-slow. If your purpose for using a language
other than whatever high-level language you prefer is maximum
performance (presumably without giving up readable source code), Java
isn't an ideal choice. If your high-level language of choice is Perl,
there's actually very little reason for Java at all, and the same is
true of some Lisp interpreters/compilers.

For those keen on functional programming syntax, Haskell is a better
choice than Java for performance: in fact, the only thing keeping
Haskell from performing as well as C, from what I understand, is the
current state of processor design. Similarly, O'Caml is one of the
fastest non-C languages available: it consistently, in a wide range of
benchmark tests and real-world anecdotal comparisons, executes "at least
half as quickly" as C, which is faster than it sounds.

The OP is right, though: if execution speed is your top priority, use C.
Java is an also-ran -- what people generally mean when they say that
Java is almost as fast as C is that a given application written in both
C and Java "also runs in under a second" in Java, or something to that
effect. While that may be true, there's a significant difference
between 0.023 seconds and 0.8 seconds (for hypothetical example).
 
C

Chad Perrin

Writing code that runs as fast in Java as it does in C is real work,
but it's possible.

. . the problem being that putting the same effort into optimizing a C
program will net greater performance rewards as well. The only language
I've ever seen stand up to C in head-to-head optimization comparisons
with any consistency, and even outperform it, was Delphi-style Objective
Pascal, and that's only anecdotal comparisons involving my father (who
knows Delphi's Objective Pascal better than most lifelong C programmers
know C), so the comparisons might be somewhat skewed. My suspicion is
that the compared C code can be tweaked to outperform the Object Pascal
beyond Object Pascal's ability to be tweaked for performance -- the
problem being that eventually you have to stop tweaking your code, so
sometimes the Object Pascal might be better anyway.

Java doesn't even come close to that level of performance optimization,
alas. At least, not from what I've seen.

There are some applications that will never perform as in Java (e.g.,
stuff that's heavily oriented to bit manipulation.) But for many
classes of applications (e.g., spreadsheets) Java can perform as well
as C.

Is that heavily optimized Java vs. "normal" (untweaked) C?
 
C

Chad Perrin

Doubt all you like.

Full disclaimer included:
As someone who is NOT paid to program in Java, and in fact finds Java
rather odious, and would rather write code in almost anything else that
isn't the annoyance factor equivalent of VB, I too doubt it. Aside from
not being paid to program in Java, though, I have played with Java code,
I have researched Java performance characteristics extensively in the
performance of job tasks, I've looked at hundreds of benchmarks over the
years, and I know a fair bit about programming language interpreters
and parsers in the abstract. The end result is that characterizing Java
as "at least as fast as C" in most cases and faster in many other cases
sounds like a load of (perhaps well-meaning) hooey to me.

They may be hoping for the impossible, but that doesn't mean they shouldn't
hope and that doesn't mean they shouldn't be frustrated when the stock
answer is "write it in C". The fact that Ruby and other dynamic languages
are not as fast as compiled C is not the language's fault or the user's
fault...it's an implementation flaw. It certainly may be a flaw that can't
be fixed, a problem impossible to solve, but there's nothing about a
language's design that should necessitate it being slower than any other
language. Perhaps we haven't found the right way to implement these
languages, or perhaps some of us have and others just aren't there yet.
Either way, it's not the language that's eventually the problem...it's
simply the distance from what the underlying platform wants to run that's an
issue. C is, as they put it, closer to "bare metal", but only because C is
little more than a set of macros on top of assembly code. If the underlying
processor ran YARV bytecodes, I doubt Ruby performance would be a concern.

I'd say "yes and no" to that. There are things about Ruby -- things
that I absolutely would not change -- that necessitate slower runtime
execution. For instance, for Ruby to work as specced, it needs dynamic
typing, which is simply slower in execution, because typing becomes a
part of execution. Static typing doesn't require types to be set at
execution: they can be set at compile-time, because they don't have to
change depending on runtime conditions. Thus, you add an extra step to
runtime execution a variable (pardon the pun) number of times. It's an
unavoidable execution-speed loss based on how the Ruby language is
supposed to work, and it's a characteristic of Ruby that I absolutely
would not throw away for better runtime performance. Because of this,
of course, it is effectively impossible to use persistent compiled
executables for Ruby to solve the runtime execution performance gap that
is introduced by dynamic typing as well. C'est la vie.

Other, similar difficulties arise as well. Ultimately, it's not the
fact that it's an interpreted language that is the problem. That can be
solved via a number of tricks (just-in-time compilation similar to Perl,
bytecode compilation, or even simply writing a compiler for it, for
example), if that's the only problem. The main problem is that, like
Perl, Python, and various Lisps, it's a dynamic language: it can be used
to write programs that change while they're running. To squeeze the
same performance out of Ruby that you get out of C, you'd have to remove
its dynamic characteristics, and once you do that you don't have Ruby
any longer.
 
S

Simon Kröger

Peter said:
I will run your Ruby version and the Java version that I write and post
the results here. Give us a week or so as I have other things to be doing.

Hmm, in a week this discussion will be over (ok, it will reappear some time
soon, but nevertheless) and everybody has swallowed your points.

$ ruby -v
ruby 1.8.4 (2005-12-24) [i386-mingw32]

$ time ruby latin.rb 5 > latin.txt

real 0m4.703s
user 0m0.015s
sys 0m0.000s

(this is a 2.13GHz PentiumM, 1GB RAM, forget the user and sys timings, but
'real' is for real, this is WinXP)

My point is: If you choose the right algorithm, your program will get faster by
orders of magnitudes - spending time optimizing algorithms is better than
spending the same time recoding everything in C. In a not so distance future
(when the interpreter is better optimized or perhaps YARV sees the light of day
my version will be even faster than yours. It will be easier to maintain and
more platform independent.

Of course you can port this enhanced version to C and it will be even faster,
but if you have a limited amount of time/money to spend on optimization i would
say: go for the algorithm.

To stop at least some of the flames: I like Extensions, i like them most if
they are generally useful (and fast) like gsl, NArray, whatever. The
combination of such Extensions and optimized algorithms (written in ruby) would
be my preferred solution if i had a performance critical problem that I'm
allowed to tackle in ruby.

cheers

Simon

p.s.: and if my solution is only that fast because of a bug (i really hope
not), i think my conclusions still hold true.
 
M

Martin Ellis

Chad said:
Haskell is a better choice than Java for performance:

I suspect it depends what you're doing...
in fact, the only thing keeping
Haskell from performing as well as C, from what I understand, is the
current state of processor design.

I'm interested to know more about that.
Could you elaborate? A reference would do.

Cheers
Martin
 
C

Chad Perrin

You're mixing language semantics and implementation details here. The
mechanics of method lookup is not a language feature; it's an implementation
detail. On the other hand, the logic of which method gets invoked in a
hierarchy of types is a language detail. Scoping is a language feature, but
the means by which scope is maintained is an implementation detail.

In some ways, you're right: implementation details are being mixed up
with language definition in the preceding list of features. In the case
of scoping, however, you're not entirely right with regard to "the means
by which scope is maintained". Dynamic scoping, by definition, requires
runtime scoping. Static scoping, by definition, does not. This means
that (to use Perl as an example, since I know it better than Ruby) my(),
which is used to declare variables in lexical scope, can be managed at
compile time, while local(), which is used for dynamic scope, can only
be managed at runtime -- else it will not work as advertised. That's
more than simply implementation details: implementation is, in this
case, dictated by language features.

As for the closure comment...sure, there's overhead in creating closures,
but it's *explicit* overhead. This is no different from creating the
equivalent of closures in languages that don't support them. The concept of
a closure has a clear specification and certainly increases the complexity
of a language and an underlying implementation. But that complexity may not
in itself always represent a decrease in performance, since other means of
accomplishing the same task may be even less performant. That's how it is
for any language feature...you take the good with the bad, and if you don't
use all of the good you may be less-than-optimal. If using a feature has ten
good aspects and five bad, and you only make use of five good aspects, then
your code is sub-optimal. If you use less than five, you're even worse off
and perhaps should consider doing things differently. Nothing about the
feature itself explicitly implies that performance should degrade by using
it...it's a matter of using those features wisely and making optimal use of
their good aspects, balanced with their bad aspects.

I think closures are kind of a bad example for this, actually. There's
nothing about closures that necessarily harms performance of the
language in implementation. In fact, closures are in some respects
merely a happy accident that arises as the result of other, positive
characteristics of a language that all can tend to contribute to better
performance of the implementation of a language (such as lexical scope,
which leads to better performance than dynamic scope). In fact, one of
the reasons Python doesn't have proper closures (lack of strict lexical
scoping) is also likely one of the reasons Python still tends to lag
behind Perl for performance purposes.

The only real overhead involved in closures, as far as I can see, is the
allocation of memory to a closure that doesn't go away until the program
exits or, in some implementations, until the program reaches a point
where it will absolutely, positively never need that closure again
(which is roughly the same thing for most uses of closures). A little
extra memory usage does not translate directly to performance loss. In
fact, in any halfway-decent system implementation, it really shouldn't
result in reduced performance unless you start having to swap because
you've overrun "physical RAM", I think.

The day may come when RAM is better managed so that performance gains
can be had for less memory usage, though, so I doubt this will always be
true.
 
C

Chad Perrin

This is the "value proposition" of the "Hot Spot" technology in the
Java Virtual Machine. On the fly, it looks for byte code sections that
get executed repeatedly and it then compiles them to object code,
thereby doing runtime optimization. This allows many Java server
processes to run with near-native speeds. When Ruby runs on a virtual
machine, planned for version 2, then Ruby can do that too. The JRuby
project will effectively accomplish the same goal.

This recent mania for VMs is irksome to me. The same benefits can be
had from a JIT compiler, without the attendant downsides of a VM (such
as greater persistent memory usage, et cetera).
 

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

Members online

No members online now.

Forum statistics

Threads
473,769
Messages
2,569,582
Members
45,067
Latest member
HunterTere

Latest Threads

Top