การใช้หน่วยความจำใน fortran เมื่อใช้อาร์เรย์ของชนิดที่ได้รับกับตัวชี้


13

ในโปรแกรมตัวอย่างนี้ฉันกำลังทำสิ่งเดียวกัน (อย่างน้อยฉันก็คิดอย่างนั้น) ในสองวิธีที่แตกต่างกัน ฉันใช้งานบนพีซีลีนุกซ์และตรวจสอบการใช้งานหน่วยความจำด้วยด้านบน การใช้ gfortran ฉันพบว่าในวิธีแรก (ระหว่าง "1" และ "2") หน่วยความจำที่ใช้คือ 8.2GB ในขณะที่วิธีที่สอง (ระหว่าง "2" และ "3") การใช้หน่วยความจำคือ 3.0GB ด้วยคอมไพเลอร์ของ Intel ความแตกต่างก็ยิ่งใหญ่กว่า: 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
ควรหลีกเลี่ยง "วิธีแรก" ให้มากที่สุดเท่าที่จะทำได้เพราะมีแนวโน้มที่จะเกิดการรั่วไหล (อาร์เรย์ต้องยกเลิกการจัดสรรอย่างชัดเจนเช่นเดียวกับที่คุณทำ) นอกเหนือจากความแตกต่างของประสิทธิภาพที่คุณเห็น เหตุผลเดียวที่จะใช้มันเพื่อการยึดมั่นอย่างเข้มงวดกับ Fortran 95 Allocatable ของประเภทที่ได้รับการเพิ่มใน TR 15581 แต่คอมไพเลอร์ Fortran ทั้งหมด (แม้คนที่ไม่มีคุณสมบัติ 2003) ได้สนับสนุนพวกเขาเช่น F95 + TR15581 + TR15580 ตั้งแต่ตลอดกาล .
stali

1
เหตุผลที่ต้องทำคือบางใบหน้าอาจมีมากกว่า 4 โหนด
chris

แน่นอนว่ามันสมเหตุสมผลแล้ว ฉันสันนิษฐานว่า 4 เป็นค่าคงที่
stali

คำตอบ:


6

ฉันไม่ทราบว่าคอมไพเลอร์ของ Fortran ทำงานอย่างไร แต่ขึ้นอยู่กับคุณสมบัติของภาษาฉันสามารถเดาได้

อาร์เรย์แบบไดนามิกใน fortran มาพร้อมกับ meta-data เพื่อทำงานกับฟังก์ชั่นภายในเช่นรูปร่าง, ขนาด, lbound, ubound และการจัดสรรหรือเชื่อมโยง (ปันส่วนได้กับพอยน์เตอร์) สำหรับอาร์เรย์ขนาดใหญ่ขนาดของ meta-data นั้นเล็กน้อย แต่สำหรับอาร์เรย์ขนาดเล็กเช่นในกรณีของคุณสามารถเพิ่มได้ ในกรณีของคุณอาร์เรย์ไดนามิกขนาด 4 น่าจะมีเมตาดาต้ามากกว่าข้อมูลจริงซึ่งนำไปสู่บอลลูนการใช้หน่วยความจำของคุณ

ฉันขอแนะนำอย่างยิ่งกับหน่วยความจำแบบไดนามิกที่ด้านล่างของโครงสร้างของคุณ หากคุณกำลังเขียนโค้ดที่เกี่ยวกับระบบทางกายภาพในบางมิติคุณสามารถตั้งค่าให้เป็นมาโครและคอมไพล์ใหม่ได้ หากคุณจัดการกับกราฟคุณสามารถจัดสรรขอบเขตบนให้กับจำนวนขอบหรือไลค์แบบคงที่ หากคุณกำลังจัดการกับระบบที่ต้องการการควบคุมหน่วยความจำแบบไดนามิกที่ละเอียดกว่าจริงคุณควรเปลี่ยนมาใช้ C


ใช่ แต่ไม่อาร์กิวเมนต์เมตาดาต้าถือเป็นจริงสำหรับทั้งสองกรณี?
stali

@ ไม่ทราบว่ากรณีที่สองต้องใช้ตัวชี้เดียวซึ่งตรงข้ามกับnตัวชี้ที่ต้องการโดยวิธีแรก
Aron Ahmadia

ฉันเพิ่มข้อมูลพื้นหลังบางส่วนแล้ว คำแนะนำของคุณในการจัดสรรขอบเขตคงที่เป็นการปรับปรุงที่ดีอยู่แล้ว ผูกพันบนเป็น 8 แต่ส่วนใหญ่จะมี 4 เพียงร้อยละขนาดเล็กจะมี 5,6,7 หรือ 8 ดังนั้นหน่วยความจำยังคงสูญเสีย ...
คริส

@chris: คุณสามารถสร้างรายการสองรายการหนึ่งรายการมี 4 รายการและรายการหนึ่งมี 8 โหนดได้หรือไม่
Pedro

อาจ. ดูเหมือนว่าจะเป็นการประนีประนอมที่ดี
chris

5

ตามที่maxhutchได้ชี้ให้เห็นปัญหาอาจเกิดจากการจัดสรรหน่วยความจำแยกต่างหาก ที่ด้านบนของข้อมูลเมตาอาจมีข้อมูลเพิ่มเติมใด ๆ และจัดตำแหน่งตัวจัดการหน่วยความจำที่ต้องการเช่นอาจเป็นการปัดเศษการจัดสรรแต่ละครั้งจนถึงหลายไบต์ 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

Fortran ของฉันเป็นสนิมเล็กน้อย แต่ข้างต้นควรทำงานถ้าไม่ได้อยู่ในหลักการ

คุณยังคงมีค่าใช้จ่ายในสิ่งที่คอมไพเลอร์ Fortran ของคุณคิดว่ามันจำเป็นต้องเก็บไว้สำหรับประเภท POINTER แต่คุณจะไม่มีตัวจัดการหน่วยความจำโอเวอร์เฮด


สิ่งนี้ช่วยได้เพียงเล็กน้อยเท่านั้น ปัญหาคือมันไม่ได้เป็นตัวชี้เดียว แต่เป็นตัวชี้แบบไดนามิก: โหนด FaceList (i)% (1: FaceList (i)% nnodes) => theNode (นิ้ว: นิ้ว + FaceList (i)% nnodes-1) นอกจากนี้ยังหมายถึงการประมาณการที่คมชัดสำหรับขนาดของอาร์เรย์ที่จัดสรรไว้ล่วงหน้า
chris

@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 ในโหมดวางจำหน่ายบน intel fortran complier นั่นใหญ่กว่า 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

การรั่วไหลของหน่วยความจำไม่ได้เกิดขึ้นเมื่อฉันใช้คำสั่ง allocatable หรือตัวชี้โดยไม่มีประเภทที่ได้รับ ในความคิดของฉันถ้าฉันประกาศตัวแปรประเภท allocatable หรือตัวชี้ในประเภทที่ได้รับและขนาดใหญ่จัดสรรตัวแปรประเภทที่ได้รับไม่ตัวแปรที่จัดสรรได้ในประเภทที่ได้รับการรั่วไหล memeory เกิดขึ้น เพื่อแก้ไขปัญหานี้ฉันเปลี่ยนรหัสของฉันที่ไม่รวมประเภทที่ได้รับตามรหัส

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))

ตัวแปร NumNodes หมายถึงจำนวนโหนดในแต่ละหน้าและตัวแปรโหนดคือหมายเลขโหนดที่ตรงกับตัวแปร NumNodes ฉันคิดว่าบางทีการรั่วไหลของหน่วยความจำจะไม่เกิดขึ้นในสไตล์โค้ดนี้

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.