मैंने हास्केल के साथ एक डीएलएल बनाया है। यह दो कार्यों का निर्यात करता है:

  • readXLSX: तर्क के रूप में एक xlsx फ़ाइल नाम, एक पत्रक का नाम लेता है, और एक R सूची में शीट की सामग्री लौटाता है

  • readXLSXbig: कोई तर्क नहीं, यह readXLSX के समान कार्य है लेकिन यह हमेशा big.xlsx नाम की फ़ाइल के Sheet 1 नाम की शीट को पढ़ता है।

सबसे पहले, एक छोटी फ़ाइल पर readXLSX प्रयास करते हैं:

> library(readxl)
> library(microbenchmark)
> microbenchmark(
+   readxl = read_xlsx("example.xlsx", "Sheet1", col_types="list", col_names=FALSE),
+   haskell = .Call("readXLSX", "example.xlsx", "Sheet1"),
+   times=2
+ )
Unit: milliseconds
    expr      min       lq     mean   median        uq       max neval cld
  readxl 9.294365 9.294365 9.660375 9.660375 10.026385 10.026385     2   b
 haskell 2.164942 2.164942 2.593681 2.593681  3.022419  3.022419     2  a 

खैर, यह readxl से तेज़ है (आश्चर्य की बात नहीं है क्योंकि readxl फ़ाइल के अस्तित्व के लिए अधिक सुविधाएँ, और परीक्षण प्रदान करता है, आदि)।

अब, फ़ाइल big.xlsx पर कोशिश करते हैं, जो बड़ी है:

> microbenchmark(
+   readxl = read_xlsx("big.xlsx", "Sheet 1", col_types="list", col_names=FALSE),
+   H1 = .Call("readXLSXbig"),
+   H2 = .Call("readXLSX", "big.xlsx", "Sheet 1"), 
+   times=2
+ )
Unit: milliseconds
   expr        min         lq       mean     median        uq       max neval cld
 readxl  143.74213  143.74213  144.05025  144.05025  144.3584  144.3584     2  a 
     H1   33.38596   33.38596   68.60333   68.60333  103.8207  103.8207     2  a 
     H2 8845.17038 8845.17038 9149.19542 9149.19542 9453.2205 9453.2205     2   b

वास्तव में, इस बेंचमार्क के परिणाम काफी परिवर्तनशील हैं (मुझे नहीं पता क्यों) लेकिन मैं हमेशा एक ही व्यवहार का पालन करता हूं: readXLSXbig readxl से तेज है और readXLSX बहुत धीमा है।

मुझे याद है कि readXLSXbig ठीक वैसा ही करता है जैसा readXLSX करता है; अंतर केवल इतना है कि फ़ाइल का नाम big.xlsx और शीट का नाम Sheet 1 पहले से ही readXLSXbig में दिया गया है:

readXLSXbig :: SEXP V 'R.Vector
readXLSXbig = unsafePerformIO $ do 
  fcellmap <- xlsxSheetToFormattedCellMap "big.xlsx" "Sheet 1"
  formattedCellMapToRList fcellmap fcellToCellValue 0

readXLSX :: SEXP s 'R.String -> SEXP s 'R.String -> SEXP V 'R.Vector
readXLSX file sheet = unsafePerformIO $ do 
  fcellmap <- xlsxSheetToFormattedCellMap (fromSEXP file) (fromSEXP sheet)
  formattedCellMapToRList fcellmap fcellToCellValue 0

ऐसा अंतर क्यों? readXLSX बड़ी फ़ाइल पर ~9 सेकंड लेता है जबकि readXLSXbig केवल ~70 मिलीसेकंड लेता है। छोटी फ़ाइल example.xlsx पर, हमने देखा है कि readXLSX <3 मिलीसेकंड लेता है, इसलिए फ़ाइल का नाम और शीट का नाम पढ़ने में 9 सेकंड बर्बाद नहीं होते हैं।

1
Stéphane Laurent 28 सितंबर 2017, 14:59
1
मुझे नहीं लगता कि आपके द्वारा प्रदान की गई जानकारी के साथ यह प्रश्न उत्तरदायी है। यह आर और आपके कोड (या सामान्य रूप से केवल आपका कोड) के बीच कुछ बातचीत से संबंधित होगा, और आपने अपना कोड प्रदान नहीं किया था।
 – 
Mike Stanley
28 सितंबर 2017, 15:09
यह संभव है लेकिन कोड बहुत बड़ा है। इसके अलावा यह inline-r पुस्तकालय का गहनता से उपयोग करता है और लगभग कोई भी इस पुस्तकालय का उपयोग नहीं करता है। यह एफएफआई के साथ कुछ सी कार्यों को भी बुलाता है ... मैं आपकी टिप्पणी को समझता हूं लेकिन शायद किसी को कारण का अंदाजा हो सकता है।
 – 
Stéphane Laurent
28 सितंबर 2017, 15:12
1
यदि परिणाम बहुत अधिक परिवर्तनशील हैं, तो शायद आपको times=2 बढ़ाना चाहिए। इसके अलावा, प्रदर्शन समस्या हास्केल पक्ष पर हो सकती है: क्या आप सुनिश्चित हैं कि दो हास्केल कार्यों के बीच कोई अंतर नहीं है, इस तथ्य को छोड़कर कि कोई निश्चित तर्कों का उपयोग करता है? स्पष्ट रूप से foo2 = foo1 fixedArg1 fixedArg2 देने का प्रयास करें और देखें कि क्या यह मायने रखता है।
 – 
chi
28 सितंबर 2017, 15:12
हां मुझे यकीन है। मेरा संपादन देखें, मैंने कोड के इस टुकड़े को शामिल किया है।
 – 
Stéphane Laurent
28 सितंबर 2017, 15:15

1 उत्तर

सबसे बढ़िया उत्तर

टीएल; डीआर: unsafe सामान unsafe है।

यह कोड

readXLSXbig :: SEXP V 'R.Vector
readXLSXbig = unsafePerformIO $ do ...

एक स्थिर अभिव्यक्ति की घोषणा करता है, जिसका मूल्यांकन पूरे कार्यक्रम में केवल एक बार किया जाएगा। पहली कॉल के बाद प्रत्येक बाद की कॉल या तो do ... क्रिया को फिर से निष्पादित कर सकती है, या कुछ भी फिर से निष्पादित किए बिना पहले से गणना किए गए मान को वापस कर सकती है।

अनिवार्य रूप से, GHC आपकी संपूर्ण स्प्रैडशीट को कैशिंग कर रहा है।

unsafePerformIO action कंपाइलर को बताता है कि हमें इस बात की परवाह नहीं है कि action कब चलाया जाता है, या कितनी बार चलाया जाता है, क्योंकि IO action कभी भी महत्वपूर्ण साइड इफेक्ट नहीं करेगा, और हमेशा रहेगा एक ही परिणाम लौटाएं। हम कंपाइलर को बता रहे हैं "मुझे पता है कि यह संभावित रूप से असुरक्षित है, लेकिन इसे वैसे भी करें - मैं जिम्मेदारी लूंगा"।

एक बदसूरत, बदसूरत फिक्स के रूप में, आप एक डमी तर्क जोड़ सकते हैं और प्रार्थना कर सकते हैं कि जीएचसी पिछले परिणामों को कैश न करे।

वास्तविक सुधार unsafe सामान को हटाना होगा, और एक उचित IO (...) प्रकार वापस करना होगा। इस तरह हम कंपाइलर से झूठ नहीं बोलते हैं, जो सही काम करेगा। एफएफआई मशीनरी को अभी भी आर से कॉल करने योग्य बनाना चाहिए।

3
chi 28 सितंबर 2017, 15:52
आआह जो बताता है कि readXLSXbig पहली बार कॉल करने पर धीमा क्यों होता है। मेरे पास unsafe के बिना एक संस्करण है लेकिन यह readXLSX की तरह धीमा है।
 – 
Stéphane Laurent
28 सितंबर 2017, 15:35
3
तो आखिर में धीमापन ही सही समय है :-(धन्यवाद @chi, अब मैं unsafePerformIO को बेहतर ढंग से समझता हूं।
 – 
Stéphane Laurent
28 सितंबर 2017, 15:47