For performance, write it in C

I think it’s also worth mentioning that talking about the speed of a
language is rather absurd. You can’t benchmark a language, other than
through mathematical proofs of complexity. What you can benchmark is an
implementation or compiler of a language, which will of course vary
widely
from version to version. Ruby’s current implementation may be slower
than
many other language implementations, but it’s not because it’s Ruby. C
may
be faster than just about anything, but it’s not because of C. It’s
because
the compilers and interpreters for those other languages are unable to
produce code as optimally as current C compilers–which isn’t surprising
considering how long C compilers have been around and how little work
they
actually have to do.

The whole “write it in C” thing really ends up being a cop-out. So the
current C Ruby implementation isn’t fast enough for you? Contribute your
time and resources and fix the implementation! Don’t bloody patch around
it
by leaving Ruby behind and writing C code! Demand more from your
platform!
The community and the fates will thank you.

On 7/26/06, James Edward G. II [email protected] wrote:

On Jul 26, 2006, at 11:28 AM, David P. wrote:

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.” :wink:

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

Guess I should unit test my posts… :slight_smile:

On Wed, 26 Jul 2006, Peter H. wrote:

you should take “Write it in C” seriously. Personally I like to write my
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

On Thu, Jul 27, 2006 at 12:42:46AM +0900, David P. wrote:

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?

On Wed, Jul 26, 2006 at 11:26:45PM +0900, Charles O Nutter wrote:

However I will write a Java version of the code and time it. It should
be interesting to say the least.

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.

hope and that doesn’t mean they shouldn’t be frustrated when the stock
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.

Peter H. wrote:

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.

On Wed, Jul 26, 2006 at 11:29:06PM +0900, Ryan McGovern wrote:

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).

Chad P. wrote:

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

On Thu, Jul 27, 2006 at 12:03:50AM +0900, Dean W. wrote:

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).

On Thu, Jul 27, 2006 at 12:23:23AM +0900, Charles O Nutter wrote:

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.

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.

On Wed, Jul 26, 2006 at 09:26:05PM +0900, Leslie V. wrote:

Something else to consider is the ease with which Ruby extensions can
be written in C. The first time I tried I has something running in 20
minutes.

Though if I was going to choose a (single) language for raw
performance I’d try to go with Pascal or Ada.

Pascal’s sort of an iffy proposition for me, in comparison with C. I’m
simply not sure that it can be optimized as thoroughly as C, in any
current implementations. According to its spec, it can probably
outperform C if implemented well, and Borland Delphi does a reasonably
good job of that, but it has received considerably less attention from
compiler programmers over time and as such is probably lagging in
implementation performance. It’s kind of a mixed bag, and I’d like to
get more data on comparative performance characteristics than I
currently have.

Ada, on the other hand – for circumstances in which it is most commonly
employed (embedded systems, et cetera), it does indeed tend to kick C’s
behind a bit. That may have more to do with compiler optimization than
language spec, though.

On Thu, Jul 27, 2006 at 03:55:10AM +0900, Martin Ellis wrote:

Chad P. wrote:

Haskell is a better choice than Java for performance:

I suspect it depends what you’re doing…

To clarify: I meant “on average” or “in general”. Obviously, there will
be instances where Java will outperform Haskell or, for that matter,
even C – just as there are times Perl can outperform C, for an
equivalent amount of invested programmer time, et cetera. I suspect the
same is true even of Ruby, despite its comparatively crappy execution
speed. That doesn’t change the fact that in the majority of cases,
Haskell will outperform most other languages. It is, after all, the C
of functional programming.

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.

I’m having difficulty finding citations for this that actually explain
anything, but the short and sloppy version is as follows:

Because imperative style programming had “won” the programming paradigm
battle back in the antediluvian days of programming, processors have
over time been oriented more and more toward efficient execution of code
written in that style. When a new processor design and a new
instruction set for a processor is shown to be more efficient in code
execution, it is more efficient because it has been better architected
for the software that will run on it, to better handle the instructions
that will be given to it with alacrity. Since almost all programs
written today are written in imperative, rather than functional, style,
this means that processors are optimized for execution of imperative
code (or, more specifically, execution of binaries that are compiled
from imperative code).

As a result, functional programming languages operate at a slight
compilation efficiency disadvantage – a disadvantage that has been
growing for decades. There are off-hand remarks all over the web about
how functional programming languages supposedly do not compile as
efficiently as imperative programming languages, but these statements
only tell part of the story: the full tale is that functional
programming languages do not compile as efficiently on processors
optimized for imperative-style programming.

We are likely heading into an era where that will be less strictly the
case, however, and functional languages will be able to start catching
up, performance-wise. Newer programming languages are beginning to get
further from their imperative roots, incorporating more characteristics
of functional-style languages (think of Ruby’s convergence on Lisp, for
instance). For now, however, O’Caml and, even moreso, Haskell suffer at
a disadvantage because their most efficient execution environment isn’t
available on our computers.

On Wed, Jul 26, 2006 at 08:30:11PM +0900, Jay L. wrote:

On Wed, 26 Jul 2006 17:47:13 +0900, Peter H. wrote:

In this post I want to clear some things up and provide benchmarks as to
why you should take “Write it in C” seriously.

This is a great post, and should at least be posted to a blog somewhere so
the masses who don’t know about USENET can still find it on Google!

This list is not only on USENET, for what it’s worth.

On Jul 26, 2006, at 8:03 pm, Chad P. wrote:

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).

Chad,

I’m late to this conversation but I’ve been interested in Ruby
performance lately. I just had to write a script to process about
1-1.5GB of CSV data (No major calculations, but it involves about 20
million rows, or something in that region). The Ruby implementation
I wrote takes about 2.5 hours to run - I think memory management is
the main issue as the manual garbage collection run I added after
each file goes into several minutes for the larger sets of data. As
you can imagine, I am more than eager for YARV/Rite.

Anyway, my question really is that I thought a VM was a prerequisite
or JIT? Is that not the case? And if the YARV VM is not the way to
go, what is?

Ashley

On Thu, Jul 27, 2006 at 01:20:05AM +0900, Kristof B. wrote:

True, benchmarks only measure execution speed, but they don’t show if a
given programmer will be productive in them. I think that’s also
largely a personal choice. Some people may be more productive in a
functional language, some people more in Ruby. And others even in perl… :slight_smile:

Actually, a well-defined functional syntax is a beautiful thing to
behold. UCBLogo, of all things, taught me that – and taught me to
dearly love arithmetic prefix notation. Ruby’s .method syntax is also
pretty darned nice, but I’d like to see it slightly more consistently
applied (only slightly). There’s more to syntactic and semantic style
than mere personal preference: there are concrete benefits to a
functional syntax in terms of writing consistent code, for instance.

. . . and Perl is great. Let’s not knock it just because Ruby is great
too. Well, maybe in jest, as you’ve done, but it gets a far worse
reputation than it deserves. As Paul Graham put it, ugly code is a
result of being forced to use the wrong concepts to achieve something
specific, and not of a harsh-looking syntax. Any syntax can be
harsh-looking to someone unaccustomed to it (even Ruby’s).

Charles O Nutter wrote:

I’ll lob a couple of grenades and then duck for cover.

  • Write it in C is as valid as write it in Java (as someone else
    mentioned).

Not really. In C you can quite easily use inline assembly
to do use your chips MMX/SSE/VIS/AltiVec extensions and if
you need more, interface to your GPU if you want to use it
as a coprocessor.

I don’t know of any good way of doing those in Java except
by writing native extensions in C or directly with an assembler.

Last I played with Java it didn’t have a working cross-platform
mmap, and if that’s still true, the awesome NArray+mmap Ruby
floating around is a good real-world example of this flexibility.

On Wed, 26 Jul 2006, Kroeger, Simon (ext) wrote:

$filter = $perms.map do |p|
search lines + [p], (possibs -
end

the following
advice very seriously - “Write it in C”.

Agreed, 100%, for those who want speed, speed and nothing
else there is hardly a better way.

thanks

Simon

harp:~ > time ruby latin.rb 5 > 5.out
real 0m11.170s
user 0m10.840s
sys 0m0.040s

harp:~ > uname -srm
Linux 2.4.21-40.EL i686

harp:~ > cat /proc/cpuinfo
processor : 0
vendor_id : GenuineIntel
cpu family : 15
model : 2
model name : Intel® Pentium® 4 CPU 2.40GHz
stepping : 7
cpu MHz : 2386.575
cache size : 512 KB
fdiv_bug : no
hlt_bug : no
f00f_bug : no
coma_bug : no
fpu : yes
fpu_exception : yes
cpuid level : 2
wp : yes
flags : fpu vme de pse tsc msr pae mce cx8 apic sep mtrr pge
mca cmov pat pse36 clflush dts acpi mmx fxsr sse sse2 ss ht tm
bogomips : 4757.91

harp:~ > ruby -v
ruby 1.8.4 (2005-12-01) [i686-linux]

not too shabby. definite not worth the hassle for 5 seconds of c.

-a

On Thu, Jul 27, 2006 at 03:47:48AM +0900, Simon Kröger wrote:

$ 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)

Holy crap, that’s fast.

Ashley M. wrote:

performance lately. I just had to write a script to process about
1-1.5GB of CSV data (No major calculations, but it involves about 20
million rows, or something in that region).

I’ve had tremendous results optimizing Ruby programs that process huge
piles of text. There is a range of “tricks” you can use to keep Ruby
from wasting memory, which is its real downfall. If it’s possible, given
your application, to process your CSV text in such a way that you don’t
store any transformations of the whole set in memory at once, you’ll go
orders of magnitude faster. You can even try to break your computation
up into multiple stages, and stream the intermediate results out to
temporary files. As ugly as that sounds, it will be far faster.

In regard to the whole conversation on this thread: at the end of the
day, absolute performance only matters if you can put a dollar amount on
it. That makes the uncontexted language comparisons essentially
meaningless.

In regard to YARV: I get a creepy feeling about anything that is
considered by most of the world to be the prospective answer to all
their problems. And as a former language designer, I have some reasons
to believe that a VM will not be Ruby’s performance panacea.