Ask HN: Is C/C++ really faster than Python?
I have seen many people saying they choose C/C++ over Python during programming challenges because the performance of Python is lesser compared to C/C++. How correct is this fact?
If Python is actually slower compared to C/C++, how much slower is it when we implement the same algorithm in C/C++?
Is it really necessary to use only C/C++ and not Python when solving some questions which involve a lot of computation?
115 comments
[ 5.2 ms ] story [ 192 ms ] threadThe overall solution space is complex. For example, development time on Python is about 1/2 that of C++ (the classic paper is http://page.mi.fu-berlin.de/prechelt/Biblio/jccpprt_computer... - is there something more recent?). Since algorithms can trump raw performance, it may be that in 8 hours its possible to develop a more sophisticated implementation in Python which outperforms the naive implementation in C++.
In the late 1990s, when people started using Python for traditional supercomputing problems, the phrase was to use Python to "steer" the low-level code written in a faster language. For example, NumPy is a Python API which uses C/ Fortran/ assembly code at lower levels for doing array calculations. Thus, only some people need to be skilled in both levels, while most can focus on writing Python code. The actual code has parts written in multiple languages, and not "only C/C++".
1. http://speed.pypy.org/
2. https://alexgaynor.net/2011/apr/03/my-experience-computer-la...
Previously, factual corrections to that blog post were shown in the comments but now the comments are not shown.
But the blog post doesn't tell us that Alex Gaynor never said there was any problem with a CPython bug.
Alex Gaynor's blog post tell's us that "It's also not possible to send any messages once your ticket has been marked as closed, meaning to dispute a decision you basically need to pray the maintainer reopens it for some reason."
But that's completely untrue! You can send messages when the ticket is marked closed! And you can open topics in the public forum! And you can click on a username and send email in 2 clicks.
There just wouldn't be any story to blog about, if Alex Gaynor admitted that he could easily have told me -- the bug is in CPython not in my program, so show my program -- but chose to say nothing.
It can give people very wrong idea of performance between different languages. Especially people who don't have significant software development experience.
why do you say this?
It's just the end results are even less meaningful this way.
All this is entirely within their rights, of course.
Someone else should create a competing programming language benchmark that includes all reasonable language implementations and all reasonable benchmark entries in those languages. Unfortunately, no one has bothered.
I'm genuinely asking / curious. I've only looked at the site in passing maybe a few times.
http://benchmarksgame.alioth.debian.org/play.html#languagex
cPython is necessarily slower and uses more memory than C for most tasks, and that's because cPython is implemented in C. You couldn't expect a new programming language that was implemented in Python to be faster than Python, either.
This is less of a concern for scientists and others with very tough performance requirements than it otherwise would be, in part, because cPython code can delegate C and C++ code[0]. This means portions of your program that need to be highly optimized can be implemented in C or C++, while the rest of your program can continue to be written in Python.
[0] https://docs.python.org/3/extending/extending.html
You are probably thinking of purely interpreted languages because for compiled languages this is not true. You could write a compiler for a new statically typed language in Python and that language could be orders of magnitude faster than any Python implementation.
It's easier and faster to write a program in python that in C++. So given limited programmer time, it might be possible to write a faster solution in python than in C/C++ because you have more time to optimize your algorithm instead of fighting with the compiler. If you have a big project, you can spend the extra programmer time you gain by using python by moving crucial code from python to C. The crucial code can be found by using a profiler. (Saw an article about this some time ago, but don't remember which company actually did this)
Finally, python has a lot of math libraries that offer good speed for computation intensive operations, see http://www.scipy.org/.
So yes, it's not that easy. I would suggest use Python because it programs easier/faster, and if you really run into a wall, you can always port parts to C.
Provided no FFI or extensions written in other languages, non-I/O bound interpreted CPython is 50-100x slower than C/C++. JIT compiled (PyPy, etc.) Python might be in 2-10x slower bracket.
If extensions written in C/C++ are used, then you might be effectively comparing those extensions against another C/C++ implementation.
So if your code is going to be I/O bound and/or can effectively offload all computation in modules written in C/C++ (like NumPy), then Python might be practically as fast as C/C++.
If you use pure interpreted Python and implement compute limited algorithm in it, your code is going to be 50-100x slower than C/C++.
Real life scenarios are probably somewhere in between those two extreme cases.
People only say this if they haven't implemented a language.
Look at something like PyPy. The work is absolutely brilliant, written by insanely smart people... and it's still vastly slower than C for reasons that are implicit in the language. There are just aspects of the language that are hard to deal with (a lot of hidden allocations on the heap, etc.)
Here's a good talk on the subject: https://speakerdeck.com/alex/why-python-ruby-and-javascript-...
You could write a Python compiler, but it would be difficult, and you'd need to sacrifice certain parts of the language (and/or extend it, even).
Take a look at Crystal[1]. Most Ruby code will compile without modification. Sure, some cases where type deduction is not possible (like `[]`), and certain cases of meta programming (like dynamic class creation at run-time), or accidentally calling a method on Nil (which is actually a feature to prevent runtime NPE[2]).
[1]: http://crystal-lang.org/ [2]: Null Pointer Exception, in case the acronym was ambiguous.
I didn't say it's equally easy. The problem is essentially "find fastest set of operations that give equivalent output". That can be a very hard problem to solve.
For practical purposes, some languages make it easier, some harder.
Then there is the issue of heap allocation of most if not all variables since their life times are hard if not impossible to determine. Heap allocation is fast, but not so fast that you can do it for every variable.
> Then there is the issue of heap allocation of most if not all variables since their life times are hard if not impossible to determine.
It's actually other way around. In most JITted "scripting languages", by far most variables have lifetimes that can be determined and stack allocated.
https://en.wikipedia.org/wiki/Escape_analysis
Or.. do you think that maybe you know only scripting and dynamic languages and you want to believe that at some point a solution will come to you instead you having to learn programming at a lower level?
I've seen the latter attitude in plenty of people. It is better to just bite the bullet and learn a lower level language. And there is always Julia.
Absolutely. It's just a matter of time. Javascript JITters and LuaJIT are pretty good examples already. And there's so much more that can be done!
> maybe you know only scripting and dynamic languages and you want to believe that at some point a solution will come to you instead you having to learn programming at a lower level?
I do C/C++ (20+ years of experience). And a lot of other languages. Firmware and kernel driver development. 6502 (almost 30 years ago), 68k, x86 (+SSE/AVX/etc.), ARM (+NEON/etc)...
Sufficiently low level?
> I've seen the latter attitude in plenty of people.
There's a difference between where the buck is at right now and where it's going.
I don't see any evidence that dynamic languages in general will somehow get much faster though.
Intuition and optimism is a major part of it I guess. The fact that compilers have gotten increasingly clever over time. If current tracing JITs could handle branching in the trace instead of bailing out of any unexpected branch with a guard condition, we'd be closer to native performance.
From data side, for example Google's V8 can infer hidden classes and member types, turn javascript object (read: hash) into a binary structure, akin to C struct.
Well, one idea here: I envision profiling and tracing "superJITs" that can build a model of deterministic target code data flow (including target internal state), execution flow and computation. Not looking so much at the code itself at binary level, but analyzing the critical points that actually decide or compute something.
Once the model is built, the "superJIT" will compute constraints for model validity and build AST out of it. Data flow is used to see through hash tables and other transformations and where predictable, used to build binary structs for transient and state data. Simplify and optimize. Vectorize where possible. Output executable binary. Cache to RAM and to disk.
Limitations will of course be number of execution paths and too complicated data flow taints. Rarer execution paths could be pruned and special cased, and just generating fast code for the very likely paths.
Is this particular idea realistic? Frankly, I have no idea.
Do I think something similar will be built? Yes, but of course with constraints. The performance gap might never be completely removed, but incrementally narrowed. Perhaps eventually to few percent range.
We'll see.
Manual memory management will always be faster than garbage collection.
Saying the language doesn't matter with regards to the performance is completely inaccurate.
If the types can be deducted, they'll be just as fast. And when not, you fall into a dynamic case. Such as polymorphism in OOP languages, pointers in C, etc.
> Manual memory management will always be faster than garbage collection.
That's a very strong statement. So you're saying it'll always be faster in terms of total used CPU time to use manual memory management vs. garbage collection?
I wouldn't dare to make a strong statement about memory management either way!
If you're talking about shared heap manual memory allocation, like C malloc/free style, that shared part will make sure it won't scale with CPU core count. You'll also need to track lifetimes of your object destruction. What if some other thread followed a pointer into the structure you're about to free and got pre-empted by the operating system? That calls for more thread synchronization, and puts hard limits to your scalability.
I think if CPUs with tens or hundreds of cores become popular, garbage collection might be the only realistic way to avoid object lifetime synchronization bottlenecks.
> Saying the language doesn't matter with regards to the performance is completely inaccurate.
It affects the difficulty of compiling it into a performant form. Dynamic features do make it more difficult, but just look at the progress in dynamic language compilation in recent years.
Who could have guessed for example Javascript could ever be within order of magnitude of C/C++ performance? But now it is there, and the gap is only getting narrower. In my own tests, occasionally I've seen Javascript and Lua JITters generating practically same code as "gcc -O3 -march=native" does!
There's no denying that a lot of optimisations can occur with (and for want a better term) "scripting" languages. Nor that "scripting" languages aren't serious tools for serious development these days. But I also think it's fair to say that one language would out perform another in an idiomatic like-for-like contest - simply because those languages that trade in raw CPU performance do so at the gain of development time / productivity or application portability.
Thankfully these days most target platforms are powerful enough that we don't need to go that low level in pursuit of performance. And even on many popular cloud services / web sites where micro-optimisations can quickly multiply, productivity is often still a critical metric (case in point: I've barely touched C++ in recent years yet have written thousands of lines of Go, Perl, Javascript, etc).
So rather than arguing hypothetical fringe cases, we really should be praising praising the "slower" languages for providing useful abstractions.
If it wasn't compute limited it wouldn't be a very good performance test.
This is not true. Different languages provide different guarantees, which allow implementations to safely make different optimisations. For a trivial example, c99 is a faster language than c89, because the 'restrict' keyword was added.
Can you add the keyword in appropriate places based on a set of rules when to use 'restrict' keyword? Yes? Then a hypothetical compiler could follow the same rules and get to same conclusions.
http://www.ffconsultancy.com/languages/ray_tracer/performanc...
But then as always, this benchmark may just test an implementation detail (afair OCaml is unboxing floats or float arrays (?) or maybe some of the MLTon standard lib is really slow). I seem to remember other benchmarks mirroring these results though.
Now, obviously you'd hope that people aim not to write programs that rely so subtly hopes and dreams of runtime information, but hopefully it's illustrative as a counterexample.
The Python VM is doing a fair bit more work per instruction it executes; for example, incrementing an int isn't going to be a simple register add, it's going to involve a pointer dereference, some comparisons and branches, execution of several C functions, and overhead for interpreting the VM bytecode.
That said, if you understand what Python is doing under the hood you can greatly speed up your programs and approach C++ performance in some areas.
That said, I strongly recommend all performance-sensitive code is written in C++ with wrappers to drive it from Python.
A very general approach to optimizing Python code is to look at all your loops and ask yourself which ones you can replace with library calls.
https://wiki.python.org/moin/PythonSpeed/PerformanceTips
Are you doing something that has some kind of underlying C implementation (file I/O usually)? Then it should be pretty quick.
Are you doing something that is all handled by the Python interpreter? Expect it to be slower than you'd like. e.g. for loops in Python are usually slower than list comprehensions.
It can be kind of frustration to write a bunch of code that runs lightning quick and then suddenly see your runtimes explode when you add a few more fairly trivial lines to complete off some code. I'd almost rather Python had a more consistent performance profile even if it was a bit slower on average than fastest possible, because it would make it easier to reason about the performance of your code.
So far I mainly just use the default Python implementation, but if you're really struggling with it, it's probably worth it to look into using one of the JIT or JVM implementations for more consistent speed profiles.
BTW, there's also all sorts of weird performance tweaks in the community...mostly of the form of syntax A vs. syntax B, and I've found that surprisingly few of them are "real" or offer more than trivial speedups.
In other cases, in other languages, you know what you need to do to speed things up, but Python either doesn't offer that mechanism in the language or buries it under somewhere else (try finding out how to preallocate lots of entries in a dictionary instead of incrementally growing it, it exists, but it's almost impossible to find using search words like "preallocate")
If your data is too big to fit into memory, then you're likely to be I/O bound, at which point the cost of I/O operations is likely to erase performance differences between the languages.
For example, if you modify C compiler to make integer operations clamp at min/max rather than overflowing, it will always be times slower than original at number crunching, and there would be no way to overcome that.
The same code, however, will run faster on C/C++. This is because python is doing some of the work for you, and you did that work upfront when writing the C/C++.
If you are doing something which involves a lot of computation, which all fits in RAM (using numpy in python will make that a fairer test between the two choices), then C/C++ will be faster.
Remember that a good pattern is to write your high-level logic in Python, and write a python implementation of your app, and then profile it to work out the slow parts of the code and speed them up using Cython[1], which will allow you to convert python-like code to efficient C (you can also straight up borrow C/C++ implementations from other people, too). This pattern is designed to balance minimising both programmer-time (since you will only have to code the important bits in C) and run time (since the performance-critical bits can be optimised.)
[1] http://cython.org/
(for pedants: I am referring to the CPython implementation, which is the only one which supports C/C++ extensions.)
As to whether it matters for the algorithm, it depends on the algorithm. It's almost always going to be much faster in C, but, how fast do you really need it to be? Python is very good at saving programmer time, so if performance is not particularly important, I just wouldn't worry about it.
Also I wouldn't say that the same algorithm would be faster in C, there is no reason why C++ can't be as fast as C, even idiomatic modern C++.
https://github.com/mackyle/sqlite/search?q=linked+list
https://github.com/freebsd/freebsd/search?l=c&q=linked+list
[0] http://bulldozer00.com/2012/02/09/vectors-and-lists/
I'm not suggesting like, never used linked lists or anything. I just think that most computer science is predicated on an abstract machine with predictable memory latency, and naturally people tend to design things with that in mind, while in reality, unless you're processing data sets so large that memory latency is insignificant, an O(1) operation might be way slower than an O(N) operation.
I certainly agree with you on the weight given to O notation though.
There is no way a linked list will be the fastest way to do this on modern hardware. A big part of the reason it is so fast I think is because intel cpus are so good at prefetching, caching, and instruction reordering.
If you are talking about kd-trees specifically you might want to ask questions if you have any. I've done 4 full implementations, each one with more refinement.
I see more what you are saying below but I can't reply to it. First, you could have that a vector of that struct and memmove the elements on swap, which wouldn't be too bad performance-wise. In a struct of arrays set up it wouldn't perform as well since the swaps would be multiple cache-incoherent reads and writes (sort of as you said).
Sorting an array of ints will be very fast and likely the fastest way to do it. Then you can structure your data however you want and re-write it once you've sorted it.
Introsort and quicksort can be done in place using an array, but constant spatial complexity isn't always priority one. Some of the more recent algorithms aren't constant (timsort, spreadsort).
Linked lists (doubly linked lists) still have valid uses, but it's generally better to use an array (or vector) until you can justify otherwise.
I can promise you that if there is a linked list in a performance crucial place that it could be sped up a non trivial amount.
Linked lists can be convenient and they can be consistent, but they aren't fast. The pointer chasing hurts performance. The extra heap allocations may or may not be able to be avoided. The alternative is something like a vector of pointers (although I would in practicality not being using a raw pointer, it would be some sort of larger data structure). That will be faster with the exception of resizes, which should be fast in general, since what they hold should be small, but they won't be consistent.
Beyond that there is the idea that concurrency could mean that needing to lock a data structure makes a linked list less of an outlier because the time that it would need to remain locked would be smaller. In that case I think a vector would likely still be much better in general, since you could keep the current index to write to in an atomic, which wouldn't require a lock.
Most lower-level memory allocators e.g. tend to use linked-lists afaik because you end up removing and inserting slabs a lot, the Linux kernel also makes wide use of linked lists as well. If there were substantial performance gains to be made without other large tradeoffs it would have been done.
EDIT: Some additional info: http://www.jikos.cz/jikos/Kmalloc_Internals.html
Vectors/Arrays are great when you don't care as much about the total memory consumption, but you want to reduce the amount of heap fragmentation.
A downside to using arrays like this, and likely a leading reason you don't see them used that much in the linux kernel is that every once and a while an insert is going to force a realloc which takes a relatively long time. std::vector and the like mitigate this by reserving more space than they'll need and so they minimize the # of reallocs required.
There is also performance implications to doing std::vector<myStruct> vs std::vector<myStruct*>. The later can very easily mimic the same cache coherence issues that happen with a linked list of myStructs.
So when we are talking about speed in terms of millions of items in some data crunching program, vector is almost always going to win out. But if we are talking about minimal memory footprint where we want consistent timings , linked lists are a contender.
If it weren't for pointer overhead I'd just throw everything into a graph, but 64-bit addressing makes that expensive.
Even then, that might be a good strategy if inserting into a vector type data structure required a realloc everytime. However, most implementations use an exponential strategy, so for a million item vector grown one at a time, you'd see only 10-20 reallocs. Which is to say, the realloc problem is only a problem in memory constrained -- think embedded or kernel -- systems where a memory overhead of 1.5-2x isn't viable.
You can do the same thing -- and some linked list implementations do -- with linked lists, where you grab a block of nodes at a time. If you don't though, just in inserts, the vector will be faster. Mallocs are non-trivial in most applications and can easily become the bottleneck.
I think the thing you might be underestimating is how much faster it is to iterate over a vector than a linked list, nevermind random access. Cache issues really, really do matter. If you want to see this in action and happen to have a SoC dev board around, disable the data cache and run some benchmarks with vectors. If you re-enable it, you will easily see a 10x-20x improvement.
If you watch https://www.youtube.com/watch?v=YQs6IC-vgmo, he goes into some of the use cases and some benchmarks that might be compelling to you.
Or userland, when your software is processing hundreds of millions of datapoints - each with several additional independent properties.
> I think the thing you might be underestimating is how much faster it is to iterate over a vector than a linked list...
No, I'm well aware of the effects of locality - the linked list of arrays is an easily tuned compromise between speed and memory consumption (leaning much more towards memory considerations).
I don't get the point about additional independent properties. Size of the struct changes the memory you need to grab, but not the number of reallocs you have to do.
See: http://baptiste-wicht.com/posts/2012/12/cpp-benchmark-vector... for actual benchmarks on this. If you disagree with these benchmarks, and find your own results to be different, I'd love to see the write up on that, and I'm sure I'm not the only one.
You've got hundreds of millions of data points to slurp into ram, each data point may have additional properties from a fixed set, data points are independent of one another - so not a good fit for a graph, adjacency matrix, etc. Disk access outside of the initial slurp is a no go, so we have to make this fit in ram.
Throwing the data point into a struct is nice and easy, plus it eases the system call overhead for m/re/callocs. But you've got a bunch of wasted space in padding, locality is gone, compressibility is much more limited.
Throwing the data into a bunch of parallel arrays is about the most memory efficient way of doing it, especially considering the ease with which you can use delta encoding and bin packing. But now every memory/sort operation is magnified by the number of arrays you've got.
As far as a writeup, I'm working on a succinct atomic map for strings - I look forward to releasing the code once I finish... and rewrite it in such a way that my employer doesn't sue me (it isn't organic to the business, but better safe than sorry).
Not entirely clear on how the indexing would work to your benefit with parallel arrays. If there is a 1 to 1 mapping between datapoints to items in each || array, the memory consumption is the same as a struct, so I assume thats not it.
It almost sounds like you are describing a (possibly non-numeric? non-homogenous?) sparse matrix, which are usually worked with in vastly different formats for construction vs manipulation vs iterating over.
Not that I know of, sorry.
> Not entirely clear on how the indexing would work to your benefit with parallel arrays.
Lets say that we have data that would be represented in an array of structs like so:
You could also represent it as a struct of arrays: Now why would you want to use SOA over AOS (disregarding the AOS alignment padding overhead)? Locality and compression. When you want to search on age, you don't need to waste cache space stepping over name. Age can also be bin packed pretty tightly, so in reality it wouldn't be an int* - but a variably sized ADT. Ideally name wouldn't be char either, but another ADT. Transparent zlib compression isn't unheard of.> ... which are usually worked with in vastly different formats for construction vs manipulation vs iterating over.
Yup, classic abstract data type - doing OO in C the hard way :)
Functional programming is much more popular in academic circles than in industry. I think academics tend to hand wave away practical concerns like cache coherency as "an implementation detail" or as an "exercise for the reader." Big O times are often considered, but rarely do researchers make a career off of "how well does this fit into an X86 L3 cache"
So yes, functional programming tends to depend on linked lists, and that's probably why you don't see it in things like the game industry. This might change in the future, for instance, I could see it being the case where having provably immutable memory could allow processors to manage more memory efficiently, but I don't think you can have a fast language that considers the CPU as an abstract entity; you have to design these things in.
The biggest reason to write in C over C++ is simplicity and higher level of control over algorithms and data structures. Portability is another reason though.
For simple functions, templates are usually always a pure win. For example, C qsort is horrible. Function pointer to a otherwise so inlinable small sort function is always going to get beaten by C++ template based sort.
But if each iteration of loop needs to fetch 10 kB of instructions because of specialized separately compiled templated forms, suddenly pointers win again. It can also invalidate TLB, branch predictor, etc. caches.
But most cases templates are a win. Just remember microbenchmarking can really mislead here, if the program as whole has an icache bottleneck as a consequence.
Python* itself is written in C, not in Python. Which should give you clues to mediate and find your own answers.
*: Python, the canonical implementation.
PyPy is written in Python and is much faster than CPython in a lot of cases, so I'm not sure the argument you're making here is valid. http://speed.pypy.org/
http://pypy.org/compat.html
PyPy is written in a restricted subset of Python called RPython, which gets translated into C
https://rpython.readthedocs.org/en/latest/faq.html
That's disingenuous. C++ is largely a superset of C, to the point that a large C project such as gcc could be compiled with a C++ compiler without too much effort:
https://lwn.net/Articles/542457/
Other implementations could theoretically do better. PyPy can detect after a while that a variable can safely be declared on the stack, and so it will start to do that. If the object type is known, then a field lookup becomes just adding an offset to the pointer. It can even use different implementations for the same Python-visible classes (to store a range result in constant space, but still appear to be the full list, or use a different type of dictionary for objects and {}, or other things like that).
If you're writing a game in python, you're going to have a bad time. Yes, I know CCP does it, but from what I've heard their engine is C++, and even then it's been a long, uphill battle. You're just going to have a very hard time meeting soft-realtime performance constraints (e.g. 30ms or 15ms frames) if very much of your code is in python.
If you're doing a programming challenge, you'll probably be fine? I've never done a programming challenge, but my assumption is that getting a result is the goal, and the performance of the code that gets it isn't that big of a deal, so long as it runs in a reasonable amount of time.
Really, the point is that you aren't going to see algorithmic-level speed differences between C/C++ and python most of the time. There are cases where this is untrue (e.g. where the algorithm relies on low level memory control), but it's true enough. You'll see a constant factor speedup, ranging somewhere between small and massive. I've heard 100x is typical for code that runs mostly in python and isn't just calling out to C, C++, or Fortran.
That said, the 100x number could be way off. For compute intensive tasks, if you're using numpy, you'll see a much smaller speedup. If you're not using numpy, but are great at writing multicore and simd code, you could see a 100(num_vector_lanes)(num_cores), so on a recent quad-core intel chip, you might be able to get a 10084 = 3200x speedup. This would take a long time to write, only applies to problems that are both compute-bound and easily parallelizable, and could be overly optimistic, but you get the idea (I don't know where the 100x number comes from, and how well the C++ programs it was compared with were written -- if it's from Alioth, last I checked they tended to leave a lot of perf on the table, but are not too bad).
Also, cache friendliness can make a massive difference. I've seen 10x speedups in C++ programs by utilizing the cache better, and I've heard of people getting even more significant speedups. Python's implementation is extremely cache unfriendly, but writing cache friendly C++ programs is non-obvious to a lot of programmers (or they don't consider it, or something), so you might loose out here. Along these lines, if your memory allocation patterns in C++ are naive, your code won't perform very well, and a lot of C++ programmers fall victim to this.
So who knows. If you're great at C/C++, know your architecture, are excellent at parallization and SIMD, and have the right problem, the difference could be massive. Barring that, I'd guess you'd get something like a 10x-50x speed up for average code, which is a lot, but maybe not that significant
Sort of rambled there, hope that made sense.
P.S. A lot of this is only true for CPython. I hear PyPy is faster (still no simd, multicore, or memory allocation or layout control though, so I suspect you can still beat it a lot of the time).
In my experience, you usually need a CPU intensive problem for the implementation language to be a speed concern. Many practical everyday problems solved with Python (outside of contests and specialized fields) tend to be I/O bound.
Secondly, even when you do have a CPU bound problem, many Python modules you might use to solve it are wrappers for C/C++ implementations.
As a result, I've come to think of Python as a control and integration language and less as a platform for base algorithm implementation.
Viewed that way, I've found Python to be unbeatable for speed of implementation and long-term maintainability (two of my top metrics for valuing code).
Python is a dynamically typed language. This means that the language will handle conversions between types depending on the context. For example, one context can yield a 64 bit integer, another a arbitrary precision integer, another a string. Keeping track of when to use one versus another, and converting between costs performance. The different data representations for each type have different performance implications as well. For example, a 64 bit integer can fit into a register, and it's generally only one cycle to perform any math on it. Arbitrary precision requires math and carry be performed for every digit in the value. With C++, types are explicit, which means there is only one type for a value, and the representation is generally the most efficient (eg 64 bit integer).
Python is also generally interpreted. This means that code can't be directly executed on the CPU. An intermediate layer, called the interpreter takes an intermediate representation of the Python code, and translates it into instructions that run on the CPU. This happens at runtime, which adds overhead for each intermediate instruction. The more instructions you need to do something, the more overhead you have. C/C++ is compiled directly into code that executes on the CPU which is typically faster. This also allows for higher levels of optimization in code, since there's an understanding of what a piece of C/C++ code will look like when it executes on the CPU. The compiler can also optimize the code further, since it knows the code (generally) won't change. It's also a lot easier to use native features of the CPU like stack allocation, which is drastically faster than heap allocation.
One of the other differences is that Python tends to provide higher levels of abstraction than C/C++ code. So, doing something like a set intersection using x.intersection(y) may not necessarily be the fastest way. Python may do a hashed compare, but sorting and using memcmp may be faster. It's a lot more code to write though. This is generally the tradeoff for Python vs. C/C++ (natively compiled code) in general.
If you're concerned with performance, but you like programming in Python, you may want to check out Haskell. It's provides a lot of the same higher level abstractions Python provides, but the language is strongly typed and it can be compiled to native code. A simple example... http://uxcn.blogspot.com/2011/11/algorithm-complexity.html.
C++ provides `set_intersection` as well: http://en.cppreference.com/w/cpp/algorithm/set_intersection
So, for example, Python may have a better algorithm, but C++ may use the hardware more efficiently.
And in C++, it provides the same high-level expressiveness as in Python.
> the more you can use them to easily do complex things arcoss different domains quickly.
But the advantage of C++ is that those abstractions have almost no overhead.
> So, for example, Python may have a better algorithm
Actually, C++ has a complexity guarantee for all of its algorithms.
> but C++ may use the hardware more efficiently.
Well, the brilliance of what Stepanov did was to build general purpose algorithms with out taking away random access capabilities which is beneficial for hardware efficiency(even more so now with modern cpus).