समानांतर में यहां कई मुद्दे चल रहे हैं, जैसा कि थे।
पहला यह है कि समांतर में किसी समस्या को हल करने में हमेशा अनुक्रमिक रूप से ऐसा करने से अधिक वास्तविक कार्य करना शामिल होता है। ओवरहेड कई धागे के बीच काम को विभाजित करने और परिणामों में शामिल होने या विलय करने में शामिल है। छोटी तारों को कम-मामले में परिवर्तित करने जैसी समस्याएं इतनी छोटी हैं कि वे समानांतर विभाजित ओवरहेड द्वारा घिरे होने के खतरे में हैं।
दूसरा मुद्दा यह है कि जावा प्रोग्राम बेंचमार्किंग बहुत सूक्ष्म है, और भ्रमित परिणाम प्राप्त करना बहुत आसान है। दो सामान्य मुद्दे जेआईटी संकलन और मृत कोड उन्मूलन हैं। लघु मानक अक्सर जेआईटी संकलन के पहले या उसके दौरान खत्म होते हैं, इसलिए वे चरम थ्रूपुट को माप नहीं रहे हैं, और वास्तव में वे खुद ही जेआईटी को माप सकते हैं। जब संकलन होता है तो कुछ हद तक गैर-निर्धारक होता है, इसलिए इससे परिणाम भी जंगली रूप से भिन्न हो सकते हैं।
छोटे, सिंथेटिक मानक के लिए, वर्कलोड अक्सर फेंकने वाले परिणामों की गणना करता है। जेआईटी कंपाइलर्स इसका पता लगाने और कोड को खत्म करने में काफी अच्छे हैं जो कहीं भी उपयोग किए जाने वाले नतीजों का उत्पादन नहीं करते हैं। यह शायद इस मामले में नहीं हो रहा है, लेकिन यदि आप अन्य सिंथेटिक वर्कलोड के साथ घूमते हैं, तो यह निश्चित रूप से हो सकता है। बेशक, यदि जेआईटी बेंचमार्क वर्कलोड को हटा देता है, तो यह बेंचमार्क बेकार प्रदान करता है।
मैं दृढ़ता से एक विकसित विकसित बेंचमार्किंग ढांचे का उपयोग करने की सलाह देता हूं जैसे JMH अपने आप में से एक हाथ से रोलिंग के बजाय। जेएमएच में इन्हें आम बेंचमार्किंग नुकसान से बचने में मदद करने के लिए सुविधाएं हैं, और इसे स्थापित करना और चलाने में बहुत आसान है।
package com.stackoverflow.questions;
import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;
import java.util.concurrent.TimeUnit;
import org.openjdk.jmh.annotations.*;
public class SO23170832 {
@State(Scope.Benchmark)
public static class BenchmarkState {
static String[] array;
static {
array = new String[1000000];
Arrays.fill(array, "AbabagalamagA");
}
}
@GenerateMicroBenchmark
@OutputTimeUnit(TimeUnit.SECONDS)
public List<String> sequential(BenchmarkState state) {
return
Arrays.stream(state.array)
.map(x -> x.toLowerCase())
.collect(Collectors.toList());
}
@GenerateMicroBenchmark
@OutputTimeUnit(TimeUnit.SECONDS)
public List<String> parallel(BenchmarkState state) {
return
Arrays.stream(state.array)
.parallel()
.map(x -> x.toLowerCase())
.collect(Collectors.toList());
}
}
मैं इस का उपयोग करते हुए आदेश भागा:
java -jar dist/microbenchmarks.jar ".*SO23170832.*" -wi 5 -i 5 -f 1
(। विकल्पों पाँच वार्मअप पुनरावृत्तियों, पांच बेंचमार्क पुनरावृत्तियों से संकेत मिलता है, और एक JVM काँटेदार) अपनी दौड़ के दौरान यहाँ अपने बेंचमार्क JMH उपयोग करने के लिए बदला न गया हो , जेएमएच बहुत सारे वर्बोज़ संदेशों को उत्सर्जित करता है, जिन्हें मैंने elided किया है। सारांश परिणाम निम्नानुसार हैं।
Benchmark Mode Samples Mean Mean error Units
c.s.q.SO23170832.parallel thrpt 5 4.600 5.995 ops/s
c.s.q.SO23170832.sequential thrpt 5 1.500 1.727 ops/s
ध्यान दें कि परिणाम प्रति सेकंड ऑप्स में हैं, इसलिए ऐसा लगता है कि समानांतर रन की तरह था के बारे में तीन बार अनुक्रमिक रन की तुलना में तेजी। लेकिन मेरी मशीन में केवल दो कोर हैं। हममम। और प्रति रन औसत त्रुटि वास्तव में रनटाइम से बड़ी है! WAT? यहां कुछ ख़राब चल रहा है।
यह हमें तीसरे अंक पर लाता है। वर्कलोड पर अधिक बारीकी से देखकर, हम देख सकते हैं कि यह प्रत्येक इनपुट के लिए एक नई स्ट्रिंग ऑब्जेक्ट आवंटित करता है, और यह परिणाम को एक सूची में भी एकत्र करता है, जिसमें बहुत से पुनर्वितरण और प्रतिलिपि शामिल होती है। मुझे लगता है कि इसका परिणाम कचरा संग्रह की उचित मात्रा में होगा। हम सक्षम जीसी संदेश के साथ बेंचमार्क का पुनर्प्रसारण द्वारा इस देख सकते हैं:
[GC (Allocation Failure) 512K->432K(130560K), 0.0024130 secs]
[GC (Allocation Failure) 944K->520K(131072K), 0.0015740 secs]
[GC (Allocation Failure) 1544K->777K(131072K), 0.0032490 secs]
[GC (Allocation Failure) 1801K->1027K(132096K), 0.0023940 secs]
# Run progress: 0.00% complete, ETA 00:00:20
# VM invoker: /Users/src/jdk/jdk8-b132.jdk/Contents/Home/jre/bin/java
# VM options: -verbose:gc
# Fork: 1 of 1
[GC (Allocation Failure) 512K->424K(130560K), 0.0015460 secs]
[GC (Allocation Failure) 933K->552K(131072K), 0.0014050 secs]
[GC (Allocation Failure) 1576K->850K(131072K), 0.0023050 secs]
[GC (Allocation Failure) 3075K->1561K(132096K), 0.0045140 secs]
[GC (Allocation Failure) 1874K->1059K(132096K), 0.0062330 secs]
# Warmup: 5 iterations, 1 s each
# Measurement: 5 iterations, 1 s each
# Threads: 1 thread, will synchronize iterations
# Benchmark mode: Throughput, ops/time
# Benchmark: com.stackoverflow.questions.SO23170832.parallel
# Warmup Iteration 1: [GC (Allocation Failure) 7014K->5445K(132096K), 0.0184680 secs]
[GC (Allocation Failure) 7493K->6346K(135168K), 0.0068380 secs]
[GC (Allocation Failure) 10442K->8663K(135168K), 0.0155600 secs]
[GC (Allocation Failure) 12759K->11051K(139776K), 0.0148190 secs]
[GC (Allocation Failure) 18219K->15067K(140800K), 0.0241780 secs]
[GC (Allocation Failure) 22167K->19214K(145920K), 0.0208510 secs]
[GC (Allocation Failure) 29454K->25065K(147456K), 0.0333080 secs]
[GC (Allocation Failure) 35305K->30729K(153600K), 0.0376610 secs]
[GC (Allocation Failure) 46089K->39406K(154624K), 0.0406060 secs]
[GC (Allocation Failure) 54766K->48299K(164352K), 0.0550140 secs]
[GC (Allocation Failure) 71851K->62725K(165376K), 0.0612780 secs]
[GC (Allocation Failure) 86277K->74864K(184320K), 0.0649210 secs]
[GC (Allocation Failure) 111216K->94203K(185856K), 0.0875710 secs]
[GC (Allocation Failure) 130555K->114932K(199680K), 0.1030540 secs]
[GC (Allocation Failure) 162548K->141952K(203264K), 0.1315720 secs]
[Full GC (Ergonomics) 141952K->59696K(159232K), 0.5150890 secs]
[GC (Allocation Failure) 105613K->85547K(184832K), 0.0738530 secs]
1.183 ops/s
नोट::
java -verbose:gc -jar dist/microbenchmarks.jar ".*SO23170832.*" -wi 5 -i 5 -f 1
इस तरह परिणाम देता है लाइनों #
के साथ शुरुआत सामान्य JMH उत्पादन लाइनें हैं। बाकी सभी जीसी संदेश हैं। यह पांच गर्मजोशी पुनरावृत्तियों में से पहला है, जो पांच बेंचमार्क पुनरावृत्तियों से पहले है। बाकी पुनरावृत्तियों के दौरान जीसी संदेश एक ही नस में जारी रहे। मुझे लगता है कि यह कहना सुरक्षित है कि मापा प्रदर्शन जीसी ओवरहेड का प्रभुत्व है और रिपोर्ट किए गए परिणामों पर विश्वास नहीं किया जाना चाहिए।
इस बिंदु पर यह स्पष्ट नहीं है कि क्या करना है। यह पूरी तरह से सिंथेटिक वर्कलोड है। यह स्पष्ट रूप से आवंटन और प्रतिलिपि की तुलना में वास्तविक काम करने में बहुत कम CPU समय शामिल करता है। यह कहना मुश्किल है कि आप वास्तव में यहां मापने की कोशिश कर रहे हैं। एक दृष्टिकोण एक अलग वर्कलोड के साथ आना होगा जो कुछ अर्थों में अधिक "वास्तविक" है। बेंचमार्क रन के दौरान जीसी से बचने के लिए एक और दृष्टिकोण ढेर और जीसी पैरामीटर को बदलना होगा।
+1 बहुत गहन उत्तर और सही तरीके से चलाने के लिए एक अच्छा ट्यूटोरियल * और एक माइक्रो बेंचमार्क की व्याख्या *! – assylias