टी एल; डॉ: कारण: आरटीएस और एसटीजी कहता है। समाधान: सी
इस का कारण क्या है ... से तुच्छ हास्केल कार्यों कहें?
अस्वीकरण: मैं कभी नहीं हास्केल सी से मैं सी और हास्केल से परिचित हूँ कहा जाता है, लेकिन मैं शायद ही कभी दोनों बटना, जब तक कि मैं एक आवरण लिख रहा हूँ। अब जब मैंने अपनी विश्वसनीयता खो दी है, तो हम बेंचमार्किंग, डिससेम्बलिंग और अन्य निफ्टी डरावनी इस साहसिक कार्य को शुरू करते हैं।
क्या अपने संसाधनों के सभी खाने है की जाँच करने के gprof
एक आसान तरीका के साथ
बेंचमार्किंग gprof उपयोग करने के लिए है। हम आपकी संकलन रेखा को थोड़ा बदल देंगे ताकि -pg
दोनों कंपाइलर और लिंकर द्वारा उपयोग किया जा सके (नोट: मैंने टेस्ट.c को main.c और test2.c का परीक्षण करने के लिए नाम दिया है।ग):
$ ghc -no-hs-main -O2 -optc-O3 -optc-pg -optl-pg -fforce-recomp \
main.c Test.hs -o test
$ ./test
$ gprof ./test
यह हमें निम्नलिखित (फ्लैट) प्रोफ़ाइल देता है:
Flat profile:
Each sample counts as 0.01 seconds.
% cumulative self self total
time seconds seconds calls Ts/call Ts/call name
16.85 2.15 2.15 scheduleWaitThread
11.78 3.65 1.50 createStrictIOThread
7.66 4.62 0.98 createThread
6.68 5.47 0.85 allocate
5.66 6.19 0.72 traverseWeakPtrList
5.34 6.87 0.68 isAlive
4.12 7.40 0.53 newBoundTask
3.06 7.79 0.39 stg_ap_p_fast
2.36 8.09 0.30 stg_ap_v_info
1.96 8.34 0.25 stg_ap_0_fast
1.85 8.57 0.24 rts_checkSchedStatus
1.81 8.80 0.23 stg_PAP_apply
1.73 9.02 0.22 rts_apply
1.73 9.24 0.22 stg_enter_info
1.65 9.45 0.21 stg_stop_thread_info
1.61 9.66 0.21 test
1.49 9.85 0.19 stg_returnToStackTop
1.49 10.04 0.19 move_STACK
1.49 10.23 0.19 stg_ap_v_fast
1.41 10.41 0.18 rts_lock
1.18 10.56 0.15 boundTaskExiting
1.10 10.70 0.14 StgRun
0.98 10.82 0.13 rts_evalIO
0.94 10.94 0.12 stg_upd_frame_info
0.79 11.04 0.10 blockedThrowTo
0.67 11.13 0.09 StgReturn
0.63 11.21 0.08 createIOThread
0.63 11.29 0.08 stg_bh_upd_frame_info
0.63 11.37 0.08 c5KU_info
0.55 11.44 0.07 stg_stk_save_n
0.51 11.50 0.07 threadPaused
0.47 11.56 0.06 dirty_TSO
0.47 11.62 0.06 ghczmprim_GHCziCString_unpackCStringzh_info
0.47 11.68 0.06 stopHeapProfTimer
0.39 11.73 0.05 stg_threadFinished
0.39 11.78 0.05 allocGroup
0.39 11.83 0.05 base_GHCziTopHandler_runNonIO1_info
0.39 11.88 0.05 stg_catchzh
0.35 11.93 0.05 freeMyTask
0.35 11.97 0.05 rts_eval_
0.31 12.01 0.04 awakenBlockedExceptionQueue
0.31 12.05 0.04 stg_ap_2_upd_info
0.27 12.09 0.04 s5q4_info
0.24 12.12 0.03 markStableTables
0.24 12.15 0.03 rts_getSchedStatus
0.24 12.18 0.03 s5q3_info
0.24 12.21 0.03 scavenge_stack
0.24 12.24 0.03 stg_ap_7_upd_info
0.24 12.27 0.03 stg_ap_n_fast
0.24 12.30 0.03 stg_gc_noregs
0.20 12.32 0.03 base_GHCziTopHandler_runIO1_info
0.20 12.35 0.03 stat_exit
0.16 12.37 0.02 GarbageCollect
0.16 12.39 0.02 dirty_STACK
0.16 12.41 0.02 freeGcThreads
0.16 12.43 0.02 rts_mkString
0.16 12.45 0.02 scavenge_capability_mut_lists
0.16 12.47 0.02 startProfTimer
0.16 12.49 0.02 stg_PAP_info
0.16 12.51 0.02 stg_ap_stk_p
0.16 12.53 0.02 stg_catch_info
0.16 12.55 0.02 stg_killMyself
0.16 12.57 0.02 stg_marked_upd_frame_info
0.12 12.58 0.02 interruptAllCapabilities
0.12 12.60 0.02 scheduleThreadOn
0.12 12.61 0.02 waitForReturnCapability
0.08 12.62 0.01 exitStorage
0.08 12.63 0.01 freeWSDeque
0.08 12.64 0.01 gcStableTables
0.08 12.65 0.01 resetTerminalSettings
0.08 12.66 0.01 resizeNurseriesEach
0.08 12.67 0.01 scavenge_loop
0.08 12.68 0.01 split_free_block
0.08 12.69 0.01 startHeapProfTimer
0.08 12.70 0.01 stg_MVAR_TSO_QUEUE_info
0.08 12.71 0.01 stg_forceIO_info
0.08 12.72 0.01 zero_static_object_list
0.04 12.73 0.01 frame_dummy
0.04 12.73 0.01 rts_evalLazyIO_
0.00 12.73 0.00 1 0.00 0.00 stginit_export_Test_zdfstableZZC0ZZCmainZZCTestZZCtest
ओह, कि कार्यों का एक समूह बुलाया हो रही है। यह आपके सी संस्करण की तुलना कैसे करता है?
$ ghc -no-hs-main -O2 -optc-O3 -optc-pg -optl-pg -fforce-recomp \
main.c TestDummy.hs test.c -o test_c
$ ./test_c
$ gprof ./test_c
Flat profile:
Each sample counts as 0.01 seconds.
% cumulative self self total
time seconds seconds calls Ts/call Ts/call name
75.00 0.05 0.05 test
25.00 0.06 0.02 frame_dummy
यह एक बहुत आसान है। पर क्यों?
क्या पीछे हो रहा है?
शायद तुम सोच किया है कि क्यों test
भी पिछले प्रोफ़ाइल में दिखाया। ठीक है, अपने आप में gprof, कुछ भूमि के ऊपर कहते हैं के रूप में objdump
साथ देखा जा सकता है:
$ objdump -D ./test_c | grep -A5 "<test>:"
0000000000405630 <test>:
405630: 55 push %rbp
405631: 48 89 e5 mov %rsp,%rbp
405634: e8 f7 d4 ff ff callq 402b30 <[email protected]>
405639: 5d pop %rbp
40563a: c3 retq
mcount
की कॉल जीसीसी से जोड़ा जाता है। तो अगले भाग के लिए आप -pg
विकल्पों को हटाना चाहते हैं। की पहली सी में disassembled test
नियमित जाँच करें:
$ ghc -no-hs-main -O2 -optc-O3 -fforce-recomp \
main.c TestDummy.hs test.c -o test_c
$ objdump -D ./test_c | grep -A2 "<test>:"
0000000000405510 <test>:
405510: f3 c3 repz retq
repz retq
वास्तव में some optimisation magic है, लेकिन इस मामले में आप एक (अधिकतर) कोई-op वापसी के रूप में इसके बारे में सोच सकते हैं।
यह कैसे हास्केल संस्करण की तुलना में कैसा
$ ghc -no-hs-main -O2 -optc-O3 -fforce-recomp \
main.c Test.hs -o test_hs
$ objdump -D ./Test.o | grep -A18 "<test>:"
0000000000405520 <test>:
405520: 48 83 ec 18 sub $0x18,%rsp
405524: e8 f7 3a 05 00 callq 459020 <rts_lock>
405529: ba 58 24 6b 00 mov $0x6b2458,%edx
40552e: be 80 28 6b 00 mov $0x6b2880,%esi
405533: 48 89 c7 mov %rax,%rdi
405536: 48 89 04 24 mov %rax,(%rsp)
40553a: e8 51 36 05 00 callq 458b90 <rts_apply>
40553f: 48 8d 54 24 08 lea 0x8(%rsp),%rdx
405544: 48 89 c6 mov %rax,%rsi
405547: 48 89 e7 mov %rsp,%rdi
40554a: e8 01 39 05 00 callq 458e50 <rts_evalIO>
40554f: 48 8b 34 24 mov (%rsp),%rsi
405553: bf 64 57 48 00 mov $0x485764,%edi
405558: e8 23 3a 05 00 callq 458f80 <rts_checkSchedStatus>
40555d: 48 8b 3c 24 mov (%rsp),%rdi
405561: e8 0a 3b 05 00 callq 459070 <rts_unlock>
405566: 48 83 c4 18 add $0x18,%rsp
40556a: c3 retq
40556b: 0f 1f 44 00 00 nopl 0x0(%rax,%rax,1)
405570: d8 ce fmul %st(6),%st
यह नहीं बल्कि अलग दिखता है। दरअसल, RTS फ़ंक्शन संदिग्ध प्रतीत होते हैं। के उन पर एक नजर है:
rts_checkSchedStatus
बस की जाँच करता है स्थिति ठीक है, और नहीं तो बाहर निकल जाता है या नहीं। Success
पथ में अधिक ओवरहेड नहीं है, इसलिए यह फ़ंक्शन वास्तव में अपराधी नहीं है।
rts_unlock
and rts_lock
मूल रूप से दावा करते हैं और capability (वर्चुअल सीपीयू) जारी करते हैं। वे newBoundTask
और boundTaskExiting
पर कॉल करते हैं, जिसमें कुछ समय लगता है (ऊपर प्रोफ़ाइल देखें)।
rts_apply
कॉल allocate
, कार्यक्रम के दौरान अक्सर उपयोग किए जाने वाले कार्यों में से एक (जो वास्तव में आश्चर्यजनक नहीं है, हैस्केल को कचरा इकट्ठा किया जा रहा है)।
rts_evalIO
अंततः वास्तविक धागा बनाता है और इसके पूरा होने के लिए प्रतीक्षा करता है। तो हम अनुमान लगा सकते हैं कि rts_evalIO
अकेले में लगभग 27% लगता है।
तो हमें हर समय लेने वाले सभी कार्यों को मिला। एसटीजी और आरटीएस प्रति कॉल 150ns के ओवरहेड के लिए सभी क्रेडिट लेते हैं।
... और क्या इस मंदी को कम करने का कोई तरीका है?
ठीक है, आपका test
मूल रूप से नो-ऑप है। आप इसे 1500 के कुल रनटाइम के साथ 100000000 बार बुला रहे हैं। सी संस्करण की तुलना में, यह प्रति कॉल ~ 14 9ns का ओवरहेड है।
समाधान काफी सरल है: छोटे सी कार्यों के लिए अपनी सी दुनिया में हास्केल फ़ंक्शंस का उपयोग न करें। सही स्थिति के लिए सही उपकरण का प्रयोग करें। आखिरकार, आप जीएमपी लाइब्रेरी का उपयोग नहीं करते हैं, यदि आपको दो संख्याओं को जोड़ने की आवश्यकता है जो 10 से कम होने की गारंटी है।
इस पैराडाइमैटिक समाधान के अलावा: नहीं। ऊपर दिखाया गया असेंबली जीएचसी द्वारा बनाया गया है, और इस समय आरटीएस कॉल के बिना एक संस्करण बनाना संभव नहीं है।
यह सुनिश्चित करने के लिए बहुत कुछ काम है कि आप हास्केल फ़ंक्शन में मेमोरी इत्यादि आवंटित कर सकते हैं। – augustss
हास्केल कोड द्वारा उत्पादित 'टेस्ट' प्रतीक को अलग करना शैक्षिक और एक महान ब्लॉग पोस्ट दोनों हो सकता है। –
प्रतिकूल जिज्ञासा से बाहर ... 'थ्रेड' के साथ संकलित करता है किसी भी तरह से कोई फर्क पड़ता है? – MathematicalOrchid