回 帖 发 新 帖 刷新版面

主题:请教一个很奇怪的问题(编译后的exe会变大)

程序代码见下,很简单的代码。我用的编译器是ivf11.060

我发现当cc数组取值不同时,编译后的exe文件大小会相差很大,百思不得其解,请教原因。



    module ta
    implicit none
!-----------------------------    
!   自定义指针数组
    type::  MT_array
        real(8),pointer::     MT(:)=>null()
    end type MT_array

!-----------------------------
!   自定义数据类型
    type tp1
    
        !若将下面两句任一句注释掉,则不会出现编译后的exe很大的情况
        type(MT_array):: ar    
        real(8):: cc(500,20000)
    
    end type tp1

    end module ta


    module tb
    use ta
    implicit none
    public 

    type(tp1):: t11   !若将此处换成指针,则不会出现编译后的exe文件很大的问题
    
    end module tb


!-------------------------------------    
    program Console1
    use tb
    implicit none
 

    ! Body of Console1
    print *, 'Hello World'

    end program Console1

回复列表 (共6个回复)

沙发

If an object of a type for which component-initialization is specified appears in the specification-part of a MODULE and does not have ALLOCATABLE or POINTER attribute, the object shall have the SAVE attribute.

板凳

在 IVF11.1 中,我并未发现 exe 文件有何变化。

3 楼

我用的11.060的IVF,确实出现这种情况啊。哪位大侠再试一下啊?多谢

4 楼

在程序中使用了大量的静态数组,最后的EXE就可能会很大。

我的 IVF 也发生了这样的问题。

500*20000*8 大概 = 76*1024*1024,也就是 76MB。

对最后的Exe进行分析,最大的区段是 .data 段,也就是数据段。数据段内的冗余数据很多,大多数压缩程序(UPX,ASPack)都可以压缩为正常的尺寸。

至于静态数组何时放入 .data 段,是否占有RAW大小,何时放入 Heap 进行动态分配?这个问题是由不同编译器而决定的。个人认为研究这个问题没有多大意义。

type(tp1):: t11 定义为指针,则不占有大量空间,因为指针在内存中只是一个 Address 或者 Descriptor。而一旦分配指针,则内存中会申请76MB的内存(注意这里的内存并不由EXE镜像映射,因此EXE文件大小正常)。

注释掉 real(8):: cc(500,20000) 由于静态数组不存在,因此EXE尺寸正常,这个很容易理解。

我们知道,EXE加载后,PE加载器从EXE结构里对文件进行内存映射。某个区段,比如 .data,其 RAW 大小被映射为虚拟大小。超出RAW的部分,映射为 00
因此,对于全部是 00 的 .data 数据,编译器可以不储存在RAW大小内,利用映射内存自动填充 00

所以,当注释掉  type(MT_array):: ar 整个结构体初始值都是 00,编译器就不会在EXE文件的 .data 段储存 76MB 的数据。

而当存在 type(MT_array):: ar 时,其内容不为空,编译器不得不在 EXE 文件里储存初始。不得不使用文件映射到内存。

空值时的EXE结构:      
No  | 名称      | 虚拟大小   | 虚拟偏移量 | 原始大小   | 原始偏移量 | 特性       | 
01  | .text     | 00062183   | 00001000   | 00063000   | 00001000   | 60000020   | 
02  | .text1    | 000000D0   | 00064000   | 00001000   | 00064000   | 60000020   | 
03  | .rdata    | 0000F1EC   | 00065000   | 00010000   | 00065000   | 40000040   | 
04  | .data     | 04C5A35C   | 00075000   | [color=red]00004000[/color]   | 00075000   | C0000040   | 
05  | .data1    | 00000020   | 04CD0000   | 00001000   | 00079000   | C0000040   | 

非空值时的EXE结构:      
No  | 名称      | 虚拟大小   | 虚拟偏移量 | 原始大小   | 原始偏移量 | 特性       | 
01  | .text     | 00062183   | 00001000   | 00063000   | 00001000   | 60000020   | 
02  | .text1    | 000000D0   | 00064000   | 00001000   | 00064000   | 60000020   | 
03  | .rdata    | 0000F1EC   | 00065000   | 00010000   | 00065000   | 40000040   | 
04  | .data     | 04C5A37C   | 00075000   | [color=red]04C4F000[/color]   | 00075000   | C0000040   | 
05  | .data1    | 00000020   | 04CD0000   | 00001000   | 04CC4000   | C0000040   | 

注意,这样的测试仅仅是针对某个编译器的。并不是 Fortran 自身的问题。
尽管两个EXE的尺寸相差很大,但运行后占有的内存,是差不多的。因为前者虽然文件小,加载到内存后,.data 段依然映射了 76MB 的大小!!!
当然了,相对而言,前者速度快一些,因为后者从文件中映射,读取硬盘需要一定的时间。

5 楼

谢谢楼上的指导。
但是,如此说来,这样的数据结构岂不是不能用了,要不然 exe文件很大的话不是很麻烦。

6 楼

[quote]谢谢楼上的指导。
但是,如此说来,这样的数据结构岂不是不能用了,要不然 exe文件很大的话不是很麻烦。[/quote]

实际上,有很多方式可以避免这样的问题。只不过IVF没有做。

你可以继续使用这样的数据结构,有很多压缩工具可以对EXE进行压缩,比如 UPX,ASPack 等,你上baidu去google一下....这样的工具压缩以后还可以正常执行,丝毫不影响EXE工作。

对于这个问题,由于压缩了空间,更会使得EXE加载更快。

而其他一些编译器,比如Ftn95,不管哪种方式,产生的EXE都非常小巧。我没有具体分析原因,可能是由于Ftn95内核都在一个DLL里,或者使用了BSS重定向。

所以,这是 IVF 的不足,而不是数据结构的问题。因为大多数情况下,我们不会傻傻的储存这样冗余的数据。

我来回复

您尚未登录,请登录后再回复。点此登录或注册