The standard improvement to apply would be to treat each i
-th bit as representing the number 2*i+1
, thus representing odds only, cutting the size of the array in half. This would also entail, for each new prime p
, starting the marking-off from p*p
and incrementing by 2*p
, to skip over evens. 2
itself is a special case. See also this question with a lot of answers.
Another strategy is to switch to the segmented sieve. That way you only need about pi(sqrt(m)) = 2*sqrt(m)/log(m)
memory (m
being your upper limit) set aside for the initial sequence of primes with which you'd sieve smaller fixed-sized array, sequentially representing segments of numbers. If you only need primes in some narrow far away range [m-d,m]
, you'd skip directly to sieving that range after all the needed primes have been gathered, as shown e.g. in this answer.
Per your specifics, to get primes up to 10^9 in value, working with one contiguous array is still possible. Using a bitarray for odds only, you'd need 10^9/16 bytes, i.e. about 60 MB of memory. Easier to work by segments; we only need 3402 primes, below 31627, to sieve each segment array below 10^9.