レックス・マン:
私は、素数を見つけるための効率的なアルゴリズムを作成しようとしてきたし、私の試みの一環として、私は次のコードを使用しています。私は私+ = 2に扇動を変更することにより、ループを高速化する考えを持っているが、これは実際にこの変更を作ることは私のプログラムの実行時間に2秒を追加しているようです。誰もが、ループが完全に仕事の仕事の半分をしなければならないだろうと思えるよう、これは、なぜ起こるか説明できますか?
for(int i = answers.get(answers.size()-1)+2;i<n;i++) {
int bit = i%64;
int currentInt = i/64;
int isPrime = (primes[currentInt] >> bit) & 1;
if(isPrime == 1) {answers.add(i);}
}
以下の完全なコード:
import java.util.ArrayList;
import java.util.List;
import java.util.stream.IntStream;
public class Primes15 {
static Long start;
public static IntStream stream() {
int numbers = 15350808;
int n = 982451712;
List<Integer> answers = new ArrayList<>();
long[] inverseShifts = new long[64];
long temp = 1;
for(int i = 0; i < inverseShifts.length; i++) {
inverseShifts[i] = temp ^ -1;
temp = temp << 1;
}
long[] primes = new long[numbers+1];
primes[0] = -6148914691370734930L;
for(int i = 1;i<primes.length; i++) {
primes[i] = -6148914691370734934L;
}
System.out.println("Setup taken " + (System.currentTimeMillis() - start) + " millis");
start = System.currentTimeMillis();
for(int p =3; p*p <=n; p+=2) {
int bit = p%64;
int currentInt = p/64;
long isPrime = (primes[currentInt] >> bit) & 1;
if(isPrime == 1) {
answers.add(p);
int cPrimeSquared = p*p;
int change = (p==2)? p : p+p;
for(int i = cPrimeSquared; i <= n; i += change) {
int innerBit = i % 64;
int innerInt = i /64;
isPrime = (primes[innerInt] >> innerBit) & 1;
if(isPrime == 1) {
primes[innerInt] = primes[innerInt] & inverseShifts[innerBit];
}
}
}
System.out.println("finder took " + (System.currentTimeMillis() - start) + " ms");
start = System.currentTimeMillis();
for(int i = answers.get(answers.size()-1)+2; i<n; i++) {
int bit = i%64;
int currentInt = i/64;
long isPrime = (primes[currentInt] >> bit) & 1;
if(isPrime == 1) {answers.add(i);}
}
System.out.println("search took " + (System.currentTimeMillis() - start) + " ms");
start = System.currentTimeMillis();
return answers.stream().mapToInt(i->i);
}
public static void main(String[] args) {
start = System.currentTimeMillis();
stream();
Long finish = System.currentTimeMillis();
System.out.println("Time taken " + (finish - start) + " millis");
}
}
user85421:
:私は簡略化され、素朴なバージョンを(素数を見つけるために使用可能ではないが、同様の計算私見で)使用し、全体のコードが行方不明になったので、まだ読みにくい - 私はJMHといくつかのテストをしました
@State(Scope.Benchmark)
@BenchmarkMode(Mode.AverageTime)
@OutputTimeUnit(TimeUnit.NANOSECONDS)
public class Increments {
private long[] primes;
@Setup
public void setup() {
primes = new long[] {3, 5, 7, 11, 13, 17, 19, 23};
}
@Benchmark
@Fork(1)
public List<Integer> inc() {
List<Integer> answers = new ArrayList<>();
for (int i = 3; i < 100; i++) {
int bit = i % 32;
int cur = i / 32;
long test = (primes[cur] >> bit) & 1;
if (test == 1) {
answers.add(i);
}
}
return answers;
}
@Benchmark
@Fork(1)
public List<Integer> addOne() {
List<Integer> answers = new ArrayList<>();
for (int i = 3; i < 100; i+=1) {
int bit = i % 32;
int cur = i / 32;
long test = (primes[cur] >> bit) & 1;
if (test == 1) {
answers.add(i);
}
}
return answers;
}
@Benchmark
@Fork(1)
public List<Integer> addTwo() {
List<Integer> answers = new ArrayList<>();
for (int i = 3; i < 100; i+=2) {
int bit = i % 32;
int cur = i / 32;
long test = (primes[cur] >> bit) & 1;
if (test == 1) {
answers.add(i);
}
}
return answers;
}
}
結果(5回のウォームアップの繰り返し、5回の反復測定):
Benchmark Mode Cnt Score Error Units Increments.addOne avgt 5 304,670 ± 73,226 ns/op Increments.addTwo avgt 5 131,429 ± 13,616 ns/op Increments.inc avgt 5 249,329 ± 14,866 ns/op
それは、操作ごとにナノ秒でソートされています。
i+=2 131ns i++ 249ns i+=1 304ns
期待の種類:2によって増分が2倍高速です。驚きのビットは、i += 1
より少し遅いですi++
-私は、コンパイラの両方に同じオペコードを作成することを想定しているだろう
JMHとの最初の接触ではなく、必ず私は100%正しくそれをしなかったが、[それをテストしなければならない場合:-)