सूचक के साथ व्युत्पन्न प्रकार की एक सरणी का उपयोग करते समय फोरट्रान में मेमोरी का उपयोग


13

इस नमूना कार्यक्रम में मैं एक ही काम कर रहा हूं (कम से कम मुझे ऐसा लगता है) दो अलग-अलग तरीकों से। मैं इसे अपने लिनक्स पीसी पर चला रहा हूं और शीर्ष के साथ मेमोरी उपयोग की निगरानी कर रहा हूं। गैफरान का उपयोग करते हुए मुझे पता चला कि पहले तरीके ("1" और "2" के बीच) में इस्तेमाल की गई मेमोरी 8.2GB है, जबकि दूसरे तरीके ("2" और "3" के बीच) में मेमोरी का उपयोग 3.0GB है। इंटेल कंपाइलर के साथ अंतर और भी बड़ा है: 10GB बनाम 3GB। यह संकेत का उपयोग करने के लिए एक अत्यधिक दंड लगता है। ऐसा क्यों होता है?

program test
implicit none

  type nodesType
    integer:: nnodes
    integer,dimension(:),pointer:: nodes 
  end type nodesType

  type nodesType2
    integer:: nnodes
    integer,dimension(4):: nodes 
  end type nodesType2

  type(nodesType),dimension(:),allocatable:: FaceList
  type(nodesType2),dimension(:),allocatable:: FaceList2

  integer:: n,i

  n = 100000000

  print *, '1'
  read(*,*)
  allocate(FaceList(n))
  do i=1,n
    FaceList(i)%nnodes = 4
    allocate(FaceList(i)%nodes(4))
    FaceList(i)%nodes(1:4) = (/1,2,3,4/)
  end do
  print *, '2'
  read(*,*)

  do i=1,n
    deallocate(FaceList(i)%nodes)
  end do
  deallocate(FaceList)

  allocate(FaceList2(n))
  do i=1,n
    FaceList2(i)%nnodes = 4
    FaceList2(i)%nodes(1:4) = (/1,2,3,4/)
  end do
  print *, '3'
  read(*,*)

end program test

पृष्ठभूमि स्थानीय ग्रिड शोधन है। मैंने चेहरों को आसानी से जोड़ने और हटाने के लिए लिंक की गई सूची को चुना। नोड्स की संख्या डिफ़ॉल्ट रूप से 4 है, लेकिन स्थानीय शोधन के आधार पर अधिक हो सकती है।


1
"पहले तरीके" से जितना संभव हो उतना बचा जाना चाहिए क्योंकि यह लीक होने का खतरा है (आपके द्वारा किए गए प्रदर्शन के अंतर के अलावा सरणियों को स्पष्ट रूप से निपटाया जाना चाहिए)। इसका उपयोग करने का एकमात्र कारण फोरट्रान 95 के सख्त पालन के लिए होगा। सभी प्रकार के व्युत्पन्न प्रकार टीआर 15581 में जोड़े गए थे, लेकिन सभी फोरट्रान संकलक (यहां तक ​​कि जिनके पास 2003 की कोई भी विशेषताएं नहीं हैं) ने उनका समर्थन किया है, अर्थात हमेशा के लिए F95 + TR95581 + TR15580 ।
stali

1
ऐसा करने का कारण यह है कि कुछ चेहरों में 4 से अधिक नोड हो सकते हैं।
क्रिस

तब यह निश्चित रूप से समझ में आता है। मैंने मान लिया कि 4 एक स्थिर था।
stali

जवाबों:


6

मैं वास्तव में नहीं जानता कि फोरट्रान कंपाइलर कैसे काम करते हैं, लेकिन भाषा की विशेषताओं के आधार पर, मैं एक अनुमान लगा सकता हूं।

फोरट्रान में गतिशील सरणियाँ मेटा-डेटा के साथ आकृति, आकार, लिबाउंड, ऊबाउंड और आंतरिक या आवंटित (संबद्ध बनाम पॉइंटर्स) जैसे आंतरिक कार्यों के साथ काम करने के लिए आती हैं। बड़े सरणियों के लिए, मेटा-डेटा का आकार नगण्य है, लेकिन छोटे सरणियों के लिए, जैसे आपके मामले में, यह जोड़ सकता है। आपके मामले में, आकार 4 गतिशील सरणियों में वास्तविक डेटा की तुलना में अधिक मेटा-डेटा होने की संभावना है, जो आपके मेमोरी उपयोग गुब्बारे के लिए अग्रणी है।

मैं आपकी संरचनाओं के निचले भाग में डायनामिक मेमोरी के खिलाफ दृढ़ता से अनुशंसा करूंगा। यदि आप एक कोड लिख रहे हैं जो भौतिक प्रणाली के साथ कुछ आयामों में काम करता है, तो आप इसे एक मैक्रो और recompile के रूप में सेट कर सकते हैं। यदि आप ग्राफ़ के साथ काम करते हैं, तो आप किनारों की संख्या या पसंद पर एक ऊपरी बाध्य को सांख्यिकीय रूप से आवंटित कर सकते हैं। यदि आप एक ऐसी प्रणाली के साथ काम कर रहे हैं, जिसे वास्तव में ठीक-ठाक गतिशील मेमोरी नियंत्रण की आवश्यकता है, तो संभवतः सी पर स्विच करना सबसे अच्छा है।


हाँ, लेकिन मेटाडेटा तर्क दोनों मामलों के लिए सही नहीं है?
stali

@stali नहीं, ध्यान दें कि दूसरे मामले में एक पॉइंटर की आवश्यकता होती है, क्योंकि nपहली विधि द्वारा आवश्यक बिंदुओं के विपरीत ।
एरन अहमदिया

मैंने कुछ पृष्ठभूमि जानकारी जोड़ी है। आपका सुझाव है कि एक ऊपरी बाउंड को आवंटित करें, पहले से ही एक अच्छा सुधार है। ऊपरी बाउंड 8 है, लेकिन बहुमत में 4 होगा, केवल एक छोटे प्रतिशत में 5,6,7 या 8 होगा। इसलिए स्मृति अभी भी बर्बाद हो गई है ...
क्रिस

@ क्रिस: क्या आप दो सूची बना सकते हैं, एक 4 के साथ, और 8 नोड्स के साथ?
पेद्रो

शायद। यह एक अच्छा समझौता है।
क्रिस

5

जैसा कि मैक्सहच ने बताया है, समस्या संभवतः अलग मेमोरी आवंटन की सरासर संख्या है। मेटाडेटा के शीर्ष पर, हालांकि, मेमोरी मैनेजर को जो भी अतिरिक्त डेटा और संरेखण की आवश्यकता होती है, वह है, अर्थात यह संभवतः प्रत्येक आवंटन को 64 बाइट्स या अधिक के कुछ एकाधिक तक राउंडिंग कर रहा है।

प्रत्येक नोड के लिए एक छोटा हिस्सा आवंटित करने से बचने के लिए, आप प्रत्येक नोड को पूर्व-आवंटित सरणी के एक हिस्से को आवंटित करने का प्रयास कर सकते हैं :

integer :: finger
indeger, dimension(8*n) :: theNodes

finger = 1
do i=1,n
    FaceList(i)%nodes => theNodes(finger:finger+FaceList(i)%nnodes-1)
    finger = finger + FaceList(i)%nnodes
end do

मेरा फोरट्रान थोड़ा कठोर है, लेकिन ऊपर काम करना चाहिए, अगर सिद्धांत में नहीं।

आपके पास अभी भी जो कुछ भी आपके फोरट्रान कंपाइलर को लगता है कि यह एक प्रकार के लिए स्टोर करने की आवश्यकता है, के ओवरहेड्स होंगे, लेकिन आपके पास मेमोरी मैनेजर ओवरहेड्स नहीं होंगे।


यह मदद करता है लेकिन केवल थोड़ा। समस्या यह है कि यह एक एकल पॉइंटर नहीं है, बल्कि पॉइंटर्स का एक गतिशील सरणी है: फेसलिस्ट (i)% नोड्स (1: फेसलिस्ट (i)% nnodes) => नोड्स (उंगली: उंगली + फेसलिस्ट (i)% nnodes-1)। यह पूर्व-आवंटित सरणी के आकार के लिए एक तेज अनुमान भी बताता है।
क्रिस

@chris: मुझे यकीन नहीं है कि मैं पूरी तरह से समझ गया हूं ... "गतिशील व्यूअर ऑफ पॉइंट" से आपका क्या मतलब है? क्षेत्र nodesType%nodesएक गतिशील सरणी के लिए एक संकेतक है।
पेड्रो

0

ओह। यह वही समस्या है जिसका मुझे सामना करना पड़ा। यह प्रश्न बहुत पुराना है, लेकिन मैं थोड़ा अलग कोड शैली का सुझाव देता हूं। मेरी समस्या आवर्ती कोड के रूप में व्युत्पन्न डेटा प्रकार में आवंटित करने योग्य स्टेटमेंट सरणी थी।

type :: node
  real*4,dimension(:),allocatable :: var4
  real*8,dimension(:),allocatable :: var8
end type node

type(node),dimension(:),allocatable :: nds

imax = 5000
allocate(nds(imax))

कुछ परीक्षण से, मैंने पुष्टि की कि यदि मैं चार प्रकारों के बारे में कोड का पालन करते हुए आबंटित कथन या पॉइंटर स्टेटमेंट का उपयोग करता हूं, तो मेमोरी लीकेज बहुत बड़ी हो जाती है। मेरे मामले में, मैंने 520MB आकार की फ़ाइल को लाल कर दिया। लेकिन मेमोरी का उपयोग इंटेल फोरट्रान पर जारी रिलीज मोड पर 4GB था। जो कि 8 गुना बड़ा है!

!(case 1) real*4,dimension(:),allocatable :: var4
!(case 2) real*4,dimension(:),pointer :: var4
!(case 3) real*4,allocatable :: var4(:,:)

!(case 4) 
type :: node(k)
  integer,len :: k = 4
  real*4 :: var4(k)
end type node

मेमोरी लीकेज तब नहीं होता है जब मैं व्युत्पन्न प्रकार के बिना आवंटन या सूचक कथन का उपयोग करता हूं। मेरी राय में, यदि मैं व्युत्पन्न प्रकार में आवर्ती या सूचक प्रकार चर की घोषणा करता हूं और बड़े प्रकार के व्युत्पन्न प्रकार को आवंटित करते हैं तो व्युत्पन्न प्रकार में आवंटन योग्य चर नहीं होता है, स्मृति रिसाव होता है। इस समस्या को हल करने के लिए, मैंने अपना कोड बदल दिया जिसमें फॉलो कोड के रूप में व्युत्पन्न प्रकार शामिल नहीं है।

real*4,dimension(:,:),allocatable :: var4 
! array index = (Num. of Nodes, Num. of Variables)

या इस शैली के बारे में कैसे?

integer,dimension(:),allocatable :: NumNodes ! (:)=Num. of Cell or Face or etc.
integer,dimension(:),allocatable :: Node     ! (:)=(Sum(NumNodes))

न्यूमोड्स वैरिएबल का अर्थ है प्रत्येक चेहरे पर नोड्स की संख्या और नोड वैरिएबल नोड संख्याएं हैं जो न्यूएनोड्स वेरिएबल से मेल खाते हैं। शायद स्मृति रिसाव इस कोड शैली में नहीं हुआ है, मुझे लगता है।

हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.