mersenneforum.org  

Go Back   mersenneforum.org > Fun Stuff > Lounge

Reply
 
Thread Tools
Old 2007-10-18, 06:54   #23
axn
 
axn's Avatar
 
Jun 2003

508210 Posts
Default

Code:
if (arr[i >> 6] & (0x80000000 >> ((i >> 1) & 0x1F)))
{
   arr[i >> 6] ^= (0x80000000 >> ((i >> 1) & 0x1F));
}
can be replaced by

Code:
arr[i >> 6] &= ~(0x80000000 >> ((i >> 1) & 0x1F))
axn is online now   Reply With Quote
Old 2007-10-18, 07:03   #24
axn
 
axn's Avatar
 
Jun 2003

2×3×7×112 Posts
Default

Also, while clearing of multiples of num, instead of starting from num * 3, you can start from num * num (Ask yourself why this would work!).

Also, the whole inner loop can be optimised by directly working with j = i/2 rather than with i. Some thing like:
Code:
for ( i = num * num, j=i>>1; (j >> 5) < b_num; j += num)
{
    arr[j >> 5] &= ~(0x80000000 >> (j & 0x1F))
}
axn is online now   Reply With Quote
Old 2007-10-18, 07:36   #25
axn
 
axn's Avatar
 
Jun 2003

2×3×7×112 Posts
Default

Quote:
Originally Posted by ShiningArcanine View Post
I had heard in the past that division operations were slow on computer processors, but the orders of magnitude difference I am seeing in performance here is ridiculous.
It is not just that the division operator is slow -- you can consider a div instruction to be 10x slower than a memory write, which would not explain the gains.

It is that, earlier, you were doing trial division -- i.e you were trying to divide every number with primes < sqrt(number), whereas SoE knows exactly which numbers are multiples of a given prime and checks off those only.

So, for example, your first version will try to check every single number to see if it is divisible by 3, whereas SoE will only check off the actual multiples of 3.

In fact, it'd be a very interesting exercise to count the inner loops of both versions -- see how many time SoE executes the "clear" operation, vs how many times the first version executed the "(num % div) == 0" operation.

Be warned, you'll probably need to use 64 bit ints to fit the count
axn is online now   Reply With Quote
Old 2007-10-18, 11:06   #26
ShiningArcanine
 
ShiningArcanine's Avatar
 
Dec 2005

22·23 Posts
Default

Quote:
Originally Posted by axn1 View Post
Code:
if (arr[i >> 6] & (0x80000000 >> ((i >> 1) & 0x1F)))
{
   arr[i >> 6] ^= (0x80000000 >> ((i >> 1) & 0x1F));
}
can be replaced by

Code:
arr[i >> 6] &= ~(0x80000000 >> ((i >> 1) & 0x1F))
That does not work, as it sets all of the bits as equal to zero, except the bit being considered. However, your next suggestion will enable me, to use:

Code:
arr[i >> 6] ^= ~(0x80000000 >> ((i >> 1) & 0x1F))
Quote:
Originally Posted by axn1 View Post
Also, while clearing of multiples of num, instead of starting from num * 3, you can start from num * num (Ask yourself why this would work!).

Also, the whole inner loop can be optimised by directly working with j = i/2 rather than with i. Some thing like:
Code:
for ( i = num * num, j=i>>1; (j >> 5) < b_num; j += num)
{
    arr[j >> 5] &= ~(0x80000000 >> (j & 0x1F))
}
My use of the following was actually a workaround because of this:

Code:
if (arr[i >> 6] & (0x80000000 >> ((i >> 1) & 0x1F)))
{
   arr[i >> 6] ^= (0x80000000 >> ((i >> 1) & 0x1F));
}
I think that j needs to be used slightly differently because this program does not have memory structures to test even integers.

So the inner loop becomes:

Code:
for ( i = num * num, j = num << 1; (i >> 6) < b_num ; i += j )
{

	arr[i >> 6] ^= (0x80000000 >> ((i >> 1) & 0x1F));

}
Quote:
Originally Posted by axn1 View Post
It is not just that the division operator is slow -- you can consider a div instruction to be 10x slower than a memory write, which would not explain the gains.

It is that, earlier, you were doing trial division -- i.e you were trying to divide every number with primes < sqrt(number), whereas SoE knows exactly which numbers are multiples of a given prime and checks off those only.

So, for example, your first version will try to check every single number to see if it is divisible by 3, whereas SoE will only check off the actual multiples of 3.

In fact, it'd be a very interesting exercise to count the inner loops of both versions -- see how many time SoE executes the "clear" operation, vs how many times the first version executed the "(num % div) == 0" operation.

Be warned, you'll probably need to use 64 bit ints to fit the count
It would be fairly humorous if I used a 32bit int and due to the overflow, the numbers came back approximately the same.

By the way, here is the latest version of my code:

Code:
/*  prime.c */
/*  Oct 17, 2007. */
/*  <my name was here> */
/*  Program to find prime numbers */
#include <stdlib.h>

int main (void)
{

	unsigned int len, num, bitVal, *arr = NULL;

	printf ("\nFind prime numbers smaller than \n");
	scanf("%i", &len);
			
	if (len < 1)
	{
				
		return 0;
			 
	}
	
	arr = (unsigned int*)calloc (blocks(len), sizeof(unsigned int));
	
	bin_arr_prime_fill(arr, len);
 
	if (len >= 2)
	{

		printf("Number 2 is a prime number\n");

	}

	/* Loop through Prime Array and Output Primes */
	for ( num = 1; num <= len ; ++arr )
	{

		for ( bitVal = 0x80000000; bitVal > 0 && num <= len; bitVal >>= 1,
num += 2 )
		{
	
			if ((*arr & bitVal) == bitVal)
			{   
	 
				printf("Number %i is a prime number\n", num);
		
			}
	 
		}
	
	}
		
	free(arr - blocks(len));
	
	main();
	
	return 0;
	
}

unsigned int blocks (const unsigned int len)
{

	return ( len % (sizeof(unsigned int) << 4) == 0 ) ?
		(len / (sizeof(unsigned int) << 4)) :
		(len / (sizeof(unsigned int) << 4) + 1);

}

int bin_arr_prime_fill (unsigned int * const arr, const unsigned int len)
{

	unsigned int num, i, j, b_num = blocks(len);

	arr[0] = 0x7FFFFFFF;

	for ( num = 1 ; num < b_num ; ++num )
	{
	
		arr[num] = 0xFFFFFFFF;

	}

	/* num is the number being checked for primiality */

	for ( num = 3; num * num <= len ; num += 2 )
	{
		
		for ( i = num * num, j = num << 1; (i >> 6) < b_num ; i += j )
		{

			arr[i >> 6] ^= (0x80000000 >> ((i >> 1) & 0x1F));

		}

	}
	
	return 0;
	
}
I had to roll back part of the blocks() function because of a bug that was introduced when I removed the modulus.

Last fiddled with by ShiningArcanine on 2007-10-18 at 11:11
ShiningArcanine is offline   Reply With Quote
Old 2007-10-18, 12:01   #27
ShiningArcanine
 
ShiningArcanine's Avatar
 
Dec 2005

22·23 Posts
Default

It seems that the inner loop's test condition can be changed from (i >> 6) < b_num to i < len, which eliminates the need for a bitshift every iteration of that loop.
ShiningArcanine is offline   Reply With Quote
Old 2007-10-18, 12:11   #28
ShiningArcanine
 
ShiningArcanine's Avatar
 
Dec 2005

22·23 Posts
Default

I had to bring back:

Code:
if (arr[i >> 6] & (0x80000000 >> ((i >> 1) % 32)))
{

	arr[i >> 6] ^= (0x80000000 >> ((i >> 1) & 0x1F));

}
I had been wrong in thinking that the other optimization eliminated the need for the conditional, as 63 gets set to true for the range 1 to 64 because 63 is a multiple of 3 and 7.

Edit: With this, the inner loop becomes:

Code:
for ( i = num * num, j = num << 1 ; i < len ; i += j )
{
			
	if (arr[i >> 6] & (0x80000000 >> ((i >> 1) & 0x1F)))
	{

		arr[i >> 6] ^= (0x80000000 >> ((i >> 1) & 0x1F));

	}

}
Edit: I really should find time to comment this code. It seems I removed an optimization when I was trying to further optimize my code. Here is the new code:

Code:
/*  prime.c */
/*  Oct 17, 2007. */
/*  <my name was here> */
/*  Program to find prime numbers */
#include <stdlib.h>

int main (void)
{

	unsigned int len, num, bitVal, *arr = NULL;

	printf ("\nFind prime numbers smaller than \n");
	scanf("%i", &len);
			
	if (len < 1)
	{

		return 0;

	}
	
	arr = (unsigned int*)calloc (blocks(len), sizeof(unsigned int));
	
	bin_arr_prime_fill(arr, len);
 
	if (len >= 2)
	{

		printf("Number 2 is a prime number\n");

	}

	/* Loop through Prime Array and Output Primes */
	for ( num = 1; num <= len ; ++arr )
	{

		for ( bitVal = 0x80000000; bitVal > 0 && num <= len; bitVal >>= 1,
num += 2 )
		{
	
			if ((*arr & bitVal) == bitVal)
			{   
	 
				printf("Number %i is a prime number\n", num);
		
			}
	 
		}
	
	}
		
	free(arr - blocks(len));
	
	main();
	
	return 0;
	
}

unsigned int blocks (const unsigned int len)
{

	return ( len % (sizeof(unsigned int) << 4) == 0 ) ?
		(len / (sizeof(unsigned int) << 4)) :
		(len / (sizeof(unsigned int) << 4) + 1);

}

int bin_arr_prime_fill (unsigned int * const arr, const unsigned int len)
{

	unsigned int num, i, j, k, b_num = blocks(len);

	arr[0] = 0x7FFFFFFF;

	for ( num = 1 ; num < b_num ; ++num )
	{
	
		arr[num] = 0xFFFFFFFF;

	}

	/* num is the number being checked for primiality */

	for ( num = 3; num * num <= len ; num += 2 )
	{

		if ((arr[num >> 6] & (0x80000000 >> ((num >> 1) & 0x1F))) == 0) 
		{

			continue;

		}
		
		for ( i = num * num, j = num << 1; i < len ; i += j )
		{
			
			if (arr[i >> 6] & (0x80000000 >> ((i >> 1) & 0x1F)))
			{

				arr[i >> 6] ^= (0x80000000 >> ((i >> 1) & 0x1F));

			}

		}

	}
	
	return 0;
	
}
I have been working on this between class, meals and sleeping.

Last fiddled with by ShiningArcanine on 2007-10-18 at 12:29
ShiningArcanine is offline   Reply With Quote
Old 2007-10-18, 12:36   #29
axn
 
axn's Avatar
 
Jun 2003

2×3×7×112 Posts
Default

Quote:
Originally Posted by ShiningArcanine View Post
That does not work, as it sets all of the bits as equal to zero, except the bit being considered. However, your next suggestion will enable me, to use:

Code:
arr[i >> 6] ^= ~(0x80000000 >> ((i >> 1) & 0x1F))
It should work. arr[i >> 6] &= ~(0x80000000 >> ((i >> 1) & 0x1F)). This is the standard way of clearing a bit. The ~ will take the NOT of the bit mask and use that to "and" out that bit. No need to check if the bit was already set or not.


Quote:
Originally Posted by ShiningArcanine View Post
I think that j needs to be used slightly differently because this program does not have memory structures to test even integers.
Already taken care in that code. That's why we start with j = i/2 and increment j by num (j += num) rather than (i += num << 1).
axn is online now   Reply With Quote
Old 2007-10-18, 15:49   #30
ShiningArcanine
 
ShiningArcanine's Avatar
 
Dec 2005

1348 Posts
Default

Quote:
Originally Posted by axn1 View Post
It should work. arr[i >> 6] &= ~(0x80000000 >> ((i >> 1) & 0x1F)). This is the standard way of clearing a bit. The ~ will take the NOT of the bit mask and use that to "and" out that bit. No need to check if the bit was already set or not.



Already taken care in that code. That's why we start with j = i/2 and increment j by num (j += num) rather than (i += num << 1).
That won't work, as & maps to AND and not XAND, such that if you have 0101 and you want to unset 0100, then ~x = 1010 and ~x AND 0100 = 0000.

If you use XAND, then (~x) XAND 0100 = 0001, but if you were to do it again, you would get what we had originally, as ~x will be 1110, and ~x XAND 0100 = 0101.

(NOT x) XAND y is both a way of setting bits and a way unsetting bits, with the bits you want to either set or unset in x set to 1 in y. That prevents it from being able to replace the conditional expression, as when the sieve goes to 64, the bit position representing 63 will be unset and reset, making the program will report that 63 is prime. However, (NOT x) XAND y == x XOR y, which is x ^ y in C.

I posted the source code. Make your change, compile it in Visual Studio, execute the program, run it for 64 as an upper bound, and watch it report that 63 is prime.

As for your other suggestion, my program only considers odd integers. On the first iteration, j is even because of j = i/2. On the second iteration, j is odd because of (j += num). On the third iteration, j is even because of (j += num). j will oscillate from even to odd over the course of the first inner loop even loop, making the program report false primes and miss primes. Of course, that is ignoring the fact that after the first iteration, the program will sieve for some even numbers, so some loops will consider only even integers and apply their values to bits that represent odd integers.

Edit: I seem to have uncovered a small bug in my program. The inner loop's condition should be i <= len and not i < len. This caused the program to generate incorrect output whenever the upper bound is an odd composite integer. Despite this bug, my 64 upper bound example above still holds. Apparently, I am somewhat biased towards picking either even or prime bounds, so I missed the bug until I accidentally typed 63 instead of 64.

Last fiddled with by ShiningArcanine on 2007-10-18 at 16:01
ShiningArcanine is offline   Reply With Quote
Old 2007-10-18, 17:40   #31
bsquared
 
bsquared's Avatar
 
"Ben"
Feb 2007

22×3×293 Posts
Default

Quote:
Originally Posted by ShiningArcanine View Post
That won't work, as & maps to AND and not XAND, such that if you have 0101 and you want to unset 0100, then ~x = 1010 and ~x AND 0100 = 0000.
I'm not familiar with XAND, (exclusive AND?? Wouldn't that always evaluate to 0?) But if you have b = 0101 and want to unset the 0100 then

Code:
b &= ~(0100)
works.

Last fiddled with by bsquared on 2007-10-18 at 17:41
bsquared is offline   Reply With Quote
Old 2007-10-18, 18:04   #32
axn
 
axn's Avatar
 
Jun 2003

2×3×7×112 Posts
Default

Quote:
Originally Posted by ShiningArcanine View Post
That won't work, as & maps to AND and not XAND, such that if you have 0101 and you want to unset 0100, then ~x = 1010 and ~x AND 0100 = 0000.

If you use XAND, then (~x) XAND 0100 = 0001, but if you were to do it again, you would get what we had originally, as ~x will be 1110, and ~x XAND 0100 = 0101.
I think you've confused which pattern is to be NOT-ed.

If you want to unset 0100 of the pattern 0101, then you do 0101 & ~(0100)
i.e. 0101 & 1011 = 0001. I am not sure why you are getting wrong results. Neither XAND nor a test for checking if the bit is set is needed here.
EDIT:- Perhaps, you were checking 63rd bit which corresponds to the prime 127?

Quote:
Originally Posted by ShiningArcanine View Post
I posted the source code. Make your change, compile it in Visual Studio, execute the program, run it for 64 as an upper bound, and watch it report that 63 is prime.
Don't have access to a C compiler

Quote:
Originally Posted by ShiningArcanine View Post
As for your other suggestion, my program only considers odd integers. On the first iteration, j is even because of j = i/2. On the second iteration, j is odd because of (j += num). On the third iteration, j is even because of (j += num). j will oscillate from even to odd over the course of the first inner loop even loop, making the program report false primes and miss primes.
The purpose of j is not to report primes; it is used to traverse the bitmap. Clearing the j-th bit of the bitmap is equivalent to clearing the integer 2*j+1. The usage of j is to merely reduce some shifts inside the critical inner loop.

Last fiddled with by axn on 2007-10-18 at 18:10
axn is online now   Reply With Quote
Old 2007-10-18, 19:00   #33
petrw1
1976 Toyota Corona years forever!
 
petrw1's Avatar
 
"Wayne"
Nov 2006
Saskatchewan, Canada

22·3·17·23 Posts
Default

Quote:
Originally Posted by wblipp View Post
You could download them and see.

http://www.rsok.com/~jrm/printprimes.html
Also note their commentary on using each bit of an 8-bit byte to respresent the primes in a range of 30 integers. I used this in my program to reduce the size of the disk file. For example even if only one of the 30 integers in a range 3,000,000,001 to 3,000,000,030 is prime you still need 4 bytes to store that number and 32 bytes if all 8 possible primes are prime. With the 8-bit method you always only need 1 byte.
petrw1 is offline   Reply With Quote
Reply

Thread Tools


Similar Threads
Thread Thread Starter Forum Replies Last Post
Finding VERY large primes c10ck3r Information & Answers 34 2012-08-29 16:47
Why arent there many softwares for finding Huge Primes blistervol Math 2 2012-08-20 17:26
Best Work for Finding Primes Unregistered Information & Answers 9 2012-06-24 13:50
Finding primes using modular stacking goatboy Math 1 2007-12-07 12:30
Finding primes with a PowerPC rogue Lounge 4 2005-07-12 12:31

All times are UTC. The time now is 11:11.


Mon Aug 2 11:11:57 UTC 2021 up 10 days, 5:40, 0 users, load averages: 1.14, 1.22, 1.39

Powered by vBulletin® Version 3.8.11
Copyright ©2000 - 2021, Jelsoft Enterprises Ltd.

This forum has received and complied with 0 (zero) government requests for information.

Permission is granted to copy, distribute and/or modify this document under the terms of the GNU Free Documentation License, Version 1.2 or any later version published by the Free Software Foundation.
A copy of the license is included in the FAQ.