patterncppMinor
Counting the number of primes in C and C++11
Viewed 0 times
numberthecountingprimesand
Problem
I found this old piece of code that I wrote while I was learning C a while back:
It's a sieve of Erathosthenes. Since now I started learning C++, I decided to rewrite it in C++11:
Is this the most idiomatic way to code in C++? I also tried to replace the first for loop with a
In both versions I am allocating on the heap because the sieve array/vector gets too big for the stack.
Interestingly, the C++ version is faster! I thought they were pretty much identical, but instead the C++ version wins by a large margin:
Feedback on the C version is also welcomed!
#include
#include
#include
int main(int argc, char *argv[]) {
if (argc \n", argv[0]);
return 1;
}
size_t limit = strtoul(argv[1], (char **) NULL, 10);
size_t i, j, res = 0;
size_t isqrt = (size_t) sqrt(limit);
char* numbers = calloc(limit, 1);
for (i = 2; i <= isqrt; i++) {
if (numbers[i]) continue;
for (j = i*i; j < limit; j += i) numbers[j] = 1;
}
for (i = 2; i < limit; i++) {
if (!numbers[i]) ++res;
}
printf("%lu\n", res);
free(numbers);
return 0;
}It's a sieve of Erathosthenes. Since now I started learning C++, I decided to rewrite it in C++11:
#include
#include
#include
#include
#include
#include
int main(int argc, char *argv[]) {
if (argc " > sieve(new std::vector(limit));
for (i = 2; i at(i)) continue;
for (j = i*i; j at(j) = 1;
}
for_each(sieve->cbegin() + 2, sieve->cend(), [&res](bool i) { res += !i; });
std::cout << res << std::endl;
return 0;
}Is this the most idiomatic way to code in C++? I also tried to replace the first for loop with a
for_each and a functor that accepts sieve, but in the end I decided it wasn't worth it and left it like that.In both versions I am allocating on the heap because the sieve array/vector gets too big for the stack.
Interestingly, the C++ version is faster! I thought they were pretty much identical, but instead the C++ version wins by a large margin:
$ gcc --std=c99 -O3 erat.c -lm -o erat
$ time ./erat 1000000000
50847534
./erat 1000000000 11.50s user 0.41s system 99% cpu 11.927 total
$ g++ --std=c++14 -O2 erat.cpp -o erat2
$ time ./erat2 1000000000
50847534
./erat2 1000000000 8.52s user 0.02s system 99% cpu 8.554 totalFeedback on the C version is also welcomed!
Solution
Idiomatic C++ Review
C++ has its own version of math header.
Difference is all the functions are placed in the
Prefer not to use
Forcing a flush is not normally what you want to do. The streams will automatically flush when required and doing it manually will only make the code less efficient.
Prefer to put one variable per line. Also for loop counters can be declared inside the for statement (see below).
Don't dynamical allocate when a local variable will do:
Also
see: Comparing std::vector to std::vector
Declare loop variables inside the
The
Unless you are using unvalidated user input there is little need to use the
In C++14 the functions
Don't bother with a return at the end of main.
#include C++ has its own version of math header.
#include Difference is all the functions are placed in the
std namespace.Prefer not to use
std::endl.std::cerr " << std::endl;Forcing a flush is not normally what you want to do. The streams will automatically flush when required and doing it manually will only make the code less efficient.
Prefer to put one variable per line. Also for loop counters can be declared inside the for statement (see below).
size_t i, j, res = 0;Don't dynamical allocate when a local variable will do:
std::unique_ptr > sieve(new std::vector(limit));
// Should be
std::vector sieve(limit);Also
std::vector is special. It is optimized for space not speed. It also has some other quirks that make it undesirable, so prefer std::vector unless you really really need to save space. BUT because of the space saving you can get better caching which may improve time. In this case presumably because of the size of the vector keeping it as bool makes it much more efficient.see: Comparing std::vector to std::vector
Declare loop variables inside the
for:for (i = 2; i <= isqrt; ++i) {
// Usually written like this:
for(int i = 2; i <= isqrt; ++i) {The
at() method is a checked access to the member. It validates your index is in range (you don't do this in the C code).if (sieve->at(i)) continue;Unless you are using unvalidated user input there is little need to use the
at() method, prefer to use operator[].In C++14 the functions
std::begin() and std::end() were introduced to get the iterators for containers. You should prefer to use these as they work with arrays as well as the standard containers.for_each(sieve->cbegin() + 2, sieve->cend(), [&res](bool i) { res += !i; });
// Can be written as:
auto begin = std::begin(sieve);
std::advance(begin, 2);
for_each(begin, std::end(sieve), [&res](bool i) { res += !i; });Don't bother with a return at the end of main.
return 0;Code Snippets
#include <math.h>#include <cmath>std::cerr << "Usage: " << argv[0] << " <N>" << std::endl;size_t i, j, res = 0;std::unique_ptr<std::vector<bool> > sieve(new std::vector<bool>(limit));
// Should be
std::vector<bool> sieve(limit);Context
StackExchange Code Review Q#128707, answer score: 7
Revisions (0)
No revisions yet.