Tôi biết rằng việc đưa ra các đánh giá về các tiêu chuẩn vi mô Java là cực kỳ nghiêm trọng, nhưng tôi thấy một cái gì đó có vẻ lạ lùng, và tôi muốn nhận được một số lời giải thích cho nó.Tại sao java lambda của tôi lại có chuyển nhượng giả nhanh hơn nhiều so với không có nó?
Lưu ý rằng tôi không sử dụng khung JMH cho việc này. Tôi biết điều đó, nhưng tôi không muốn đi theo chiều dài đó.
tôi sẽ cung cấp toàn bộ mẫu mã, nhưng trong ngắn hạn, khi tôi kiểm tra việc thực hiện hai phương pháp này
private FooPrime[] testStreamToArray(ArrayList<Foo> fooList) {
return (FooPrime[]) fooList.stream().
map(it -> {
return new FooPrime().gamma(it.getAlpha() + it.getBeta());
}).
toArray(FooPrime[]::new);
}
private FooPrime[] testStreamToArray2(ArrayList<Foo> fooList) {
return (FooPrime[]) fooList.stream().
map(it -> {
int stuff = it.getAlpha().length();
return new FooPrime().gamma(it.getAlpha() + it.getBeta());
}).
toArray(FooPrime[]::new);
}
Tôi tìm thấy kết quả rất đáng ngạc nhiên. Trong mẫu mã lớn hơn, tôi đang đo bốn cách khác nhau để làm điều này, và ba cách đầu tiên rất gần với hiệu năng. Tất cả đều chạy khoảng 50 k ns mỗi lần lặp. Tuy nhiên, mẫu mã thứ hai luôn chạy dưới một nửa tổng số đó. Đúng rồi. Nó không chậm hơn, nó nhanh hơn một chút.
Việc chạy qua cho thấy số như thế này:
manualcopy:54575 ns
toarray:53617 ns
streamtoarray:52990 ns
streamtoarray2:24217 ns
Mỗi lần chạy có số tương tự như này.
Bây giờ tôi sẽ cung cấp toàn bộ lớp và lớp cơ sở. Lưu ý rằng tôi có một "ấm lên" vượt qua, nơi tôi thực hiện các phương pháp được thử nghiệm một vài nghìn lần trước khi bắt đầu timings. Cũng lưu ý rằng mặc dù điều này chạy "testStreamToArray2" cuối cùng, tôi cũng đã thử di chuyển khối đó đến bài kiểm tra đầu tiên và các con số sẽ xuất hiện tương tự. Các dòng nhận xét ra có để thuyết phục tôi rằng các phương pháp đang thực sự làm một cái gì đó (thời gian vẫn còn về cùng với những dòng không bình luận ra).
package timings;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
public class ListToArrayOfPrimesTiming {
public static void main(String[] args) {
ListToArrayOfPrimesTiming tests = new ListToArrayOfPrimesTiming(args);
tests.go();
}
public ListToArrayOfPrimesTiming(String[] args) { }
private void go() {
final ArrayList<Foo> fooList = new ArrayList<>();
for (int ctr = 0; ctr < 1000; ++ ctr) {
fooList.add(new Foo().alpha("a" + ctr).beta("b" + ctr));
}
for (int ctr = 0; ctr < 20000; ++ ctr) {
testManualCopy(fooList);
testToArray(fooList);
testStreamToArray(fooList);
testStreamToArray2(fooList);
}
int iters = 100000;
// Set<Integer> lengths = new HashSet<>();
// Set<FooPrime> distinctFooPrimes = new HashSet<>();
// lengths.clear();
// distinctFooPrimes.clear();
new TimingContainer(iters, "manualcopy", new TimingTest() {
@Override
public void run() {
FooPrime[] fooPrimeArray = testManualCopy(fooList);
// lengths.add(fooPrimeArray.length);
// distinctFooPrimes.add(fooPrimeArray[0]);
}
}).run();
// System.out.println("lengths[" + lengths + "]");
// lengths.clear();
// System.out.println("distinctFooPrimes[" + distinctFooPrimes + "]");
// distinctFooPrimes.clear();
new TimingContainer(iters, "toarray", new TimingTest() {
@Override
public void run() {
FooPrime[] fooPrimeArray = testManualCopy(fooList);
// lengths.add(fooPrimeArray.length);
// distinctFooPrimes.add(fooPrimeArray[0]);
}
}).run();
// System.out.println("lengths[" + lengths + "]");
// lengths.clear();
// System.out.println("distinctFooPrimes[" + distinctFooPrimes + "]");
// distinctFooPrimes.clear();
new TimingContainer(iters, "streamtoarray", new TimingTest() {
@Override
public void run() {
FooPrime[] fooPrimeArray = testStreamToArray(fooList);
// lengths.add(fooPrimeArray.length);
// distinctFooPrimes.add(fooPrimeArray[0]);
}
}).run();
// System.out.println("lengths[" + lengths + "]");
// lengths.clear();
// System.out.println("distinctFooPrimes[" + distinctFooPrimes + "]");
// distinctFooPrimes.clear();
new TimingContainer(iters, "streamtoarray2", new TimingTest() {
@Override
public void run() {
FooPrime[] fooPrimeArray = testStreamToArray2(fooList);
// lengths.add(fooPrimeArray.length);
// distinctFooPrimes.add(fooPrimeArray[0]);
}
}).run();
// System.out.println("lengths[" + lengths + "]");
// lengths.clear();
// System.out.println("distinctFooPrimes[" + distinctFooPrimes + "]");
// distinctFooPrimes.clear();
}
private FooPrime[] testManualCopy(ArrayList<Foo> fooList) {
FooPrime[] fooPrimeArray = new FooPrime[fooList.size()];
int index = -1;
for (Foo foo: fooList) {
++ index;
fooPrimeArray[index] = new FooPrime().gamma(foo.getAlpha() + foo.getBeta());
}
return fooPrimeArray;
}
private FooPrime[] testToArray(ArrayList<Foo> fooList) {
List<FooPrime> fooPrimeList = new ArrayList<>();
for (Foo foo: fooList) {
fooPrimeList.add(new FooPrime().gamma(foo.getAlpha() + foo.getBeta()));
}
return fooPrimeList.toArray(new FooPrime[fooList.size()]);
}
private FooPrime[] testStreamToArray(ArrayList<Foo> fooList) {
return (FooPrime[]) fooList.stream().
map(it -> {
return new FooPrime().gamma(it.getAlpha() + it.getBeta());
}).
toArray(FooPrime[]::new);
}
private FooPrime[] testStreamToArray2(ArrayList<Foo> fooList) {
return (FooPrime[]) fooList.stream().
map(it -> {
int stuff = it.getAlpha().length();
return new FooPrime().gamma(it.getAlpha() + it.getBeta());
}).
toArray(FooPrime[]::new);
}
public static FooPrime fooToFooPrime(Foo foo) {
return new FooPrime().gamma(foo.getAlpha() + foo.getBeta());
}
public static class Foo {
private String alpha;
private String beta;
public String getAlpha() { return alpha; }
public String getBeta() { return beta; }
public void setAlpha(String alpha) { this.alpha = alpha; }
public void setBeta(String beta) { this.beta = beta; }
public Foo alpha(String alpha) { this.alpha = alpha; return this; }
public Foo beta(String beta) { this.beta = beta; return this; }
}
public static class FooPrime {
private String gamma;
public String getGamma() { return gamma; }
public void setGamma(String gamma) { this.gamma = gamma; }
public FooPrime gamma(String gamma) { this.gamma = gamma; return this; }
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + ((gamma == null) ? 0 : gamma.hashCode());
return result;
}
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
FooPrime other = (FooPrime) obj;
if (gamma == null) {
if (other.gamma != null)
return false;
} else if (!gamma.equals(other.gamma))
return false;
return true;
}
@Override
public String toString() {
return "FooPrime [gamma=" + gamma + "]";
}
}
}
Và lớp cơ sở:
package timings;
public class TimingContainer {
private int iterations;
private String label;
private TimingTest timingTest;
public TimingContainer(int iterations, String label, TimingTest timingTest) {
this.iterations = iterations;
this.label = label;
this.timingTest = timingTest;
}
public void run() {
long startTime = System.nanoTime();
for (int ctr = 0; ctr < iterations; ++ ctr) {
timingTest.randomize();
timingTest.run();
}
long endTime = System.nanoTime();
long totalns = (endTime - startTime);
System.out.println(label + ":" + (totalns/iterations) + " ns");
}
}
Điều gì sẽ xảy ra nếu bạn chuyển 'testStreamToArray (fooList);' 'testStreamToArray2 (fooList);' trong '20000 iteration' của bạn? –
Hoặc chỉ để chắc chắn, chạy thử nghiệm riêng biệt cho cả hai? Tự hỏi liệu dòng phụ có thể gây ra một số tối ưu hóa, có thể hiển thị trong bytecode không? – NickL
Tôi đã thử chuyển đổi chúng trong sự hâm nóng, không có sự khác biệt. Tôi đã thử chạy với chỉ streamarray2, có kết quả tương tự. –