FME高性能编程,让你的FME快起来

本文介绍了如何通过理解计算机编译原理,特别是内存、CPU和线程进程的关系,来优化FME性能。重点讲述了FME模板执行的底层逻辑,以及如何通过细节优化和利用Python线程池提高并发线程数来提升FME处理大批量数据的速度。实例展示了如何在FME中使用线程池进行图片压缩和遥感影像特征提取,从而显著提高处理效率。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

目录

前言

一、计算机编译原理

1.内存

2.CPU和线程进程关系

3.磁盘

二、FME如何提高性能

1.FME模板执行的底层逻辑

2.通过细节提高FME性能

 3.利用python线程池提高并发线程数

总结


前言

在处理大批量数据时,往往很多同学会发现,为什么我明明数据量很大,但是我的cpu利用率却非常低,而且内存占用却非常高。这个是很多软件都会出现的通病,不光限于FME。所以在学习高性能处理数据之前,我们还得了解下一些基础的计算机原理。


一、计算机编译原理

1.内存

内存被分为了4个区,代码区,全局区,栈区,堆区。其中代码区就是存放函数体的二进制代码,由操作系统进行管理;全局区存放全局变量和静态变量以及常量;栈区由编译器自动分配释放,存放函数的参数值,局部变量等;堆区存储复杂类型(对象),一般由程序员分配释放,若程序员不释放,由垃圾回收机制回收,引用数据类型存放到堆里面。那么由此可见,我们在使用fme时,读取的所有数据都会被引入到堆区储存,等待程序运行完毕后才会被释放。我们在使用一下阻塞类转换器比如FeatureMerger,areaonarea等转换器,这种阻塞类转换器,会等所有要素全部计算完毕后才会把要素抛出到下一个转换器。所以当程序阻塞类转换器变多,那么必将引起堆区数据不能被合理释放,则会导致内存的积压。程序的执行计算,是需要在内存中寻找数据的,所以内存中数据积压的越多,则注定会导致内存无法被释放,程序则会越跑越慢。

2.CPU和线程进程关系

大家在购买CPU时,都会遇到一个核心参数,CPU核心数和线程数。在学习python,java等编程语言时,也会涉及到多线程、多进程编程。很多小白会认为二者是一致的,一个进程占用一个核心,一个线程占用一个线程。但是其实二者的本质是完全不相等的,一个进程的运行,CPU会根据进程的计算量大小和占用资源合理的分配核心数和线程数。意思就是我们一个FME模板在理论上是可以拿到很高的CPU占用的。

3.磁盘

cpu执行本身是不会直接调用磁盘数据的,会将磁盘数据先转入内存,然后再调用内存的数据。那么一块好的固态硬盘,会显著的提升磁盘数据和内存数据的交互速度,也会显著提升我们程序的执行速度。

二、FME如何提高性能

1.FME模板执行的底层逻辑

如果我们把一个写好的fmw文件用txt或者nodepad打开,我们会发现,fmw是一个明码文件。

 所有的转换器都会以标准XML格式,把参数存入,python代码则会被编译成url编码。FME本身则是一个编译器,会将fmw中的转换器直接翻译为C代码执行,python部分代码则抛给python解释器运行。所以,fme的运行本质还是C++的执行。

2.通过细节提高FME性能

fme本身就是就是支持多线程的,意思就是只要不是阻塞类转换器,fme支持无上限个转换器并发执行。其本质就是一条数据,进入转换器中,计算完毕后马上能进入下一个转换器。这样不会造成数据在内存的积压。但是很多时候,我们需要用到不少空间分析模块,但是这部分转换器基本都是阻塞的。但是也不是没有优化空间,分组功能是FME的一大特色,可以将数据安字段组进行拆分,分别计算。通过分组,我们可以将部分需求转换为非阻塞类转换器。示例如下:

比如我们有以下数据需要进行空间分析,如果我们用常规的方式

 执行时间是14秒。

 如果我们分村执行,选择分组内容,groupby

 执行时间是稍微快了一点,而且在一个分组计算完毕后,数据就被立即抛出了。

 3.利用python线程池提高并发线程数

     我会以一个示例模板来介绍,也是本篇博客主要推荐的提高性能的方式。比如我们以一个将照片压缩到指定大小的模板来做示例。

def compress_image(outfile, mb=400, quality=80, k=0.9):  
    o_size = os.path.getsize(outfile) // 1024  
    if o_size <= mb:
        return outfile
    ImageFile.LOAD_TRUNCATED_IMAGES = True  
    while o_size > mb:
        im = Image.open(outfile)
        x, y = im.size
        out = im.resize((int(x * k), int(y * k)), Image.Resampling.LANCZOS)  # 
        try:
            out.save(outfile, quality=quality)  
        except Exception as e:
            print(e)
            break
        o_size = os.path.getsize(outfile) // 1024
    return outfile

我们将这个函数放入pythoncaller中

 然后再input函数中调用

 通过path读模块把路径读进来,然后过滤出jpg,将路径传入该函数,就可以执行图片压缩了

 查询cpu,我们发现fme进程只占用了百分之5,速度可以说是非常之慢,要处理完这些数据,估计都要花几个小时。

 接下来我们引入python的线程池ThreadPoolExecutor。先将要素全部传入列表中,然后建立线程池,通过submit提交任务。

from concurrent.futures import ThreadPoolExecutor
    def __init__(self):
        self.aa=[]
        """Base constructor for class members."""
        pass

    def input(self, feature):
        self.aa.append(feature) 
    def close(self):
        
        with ThreadPoolExecutor(100) as t:
            for i in self.aa:
                t.submit(compress_image,outfile=i.getAttribute('path_windows'))
                self.pyoutput(i)
        pass        

运行模板,这时候因为多线程,cup直接被干到了百分之70。要知道,我的处理器可是i9-12900k,如果线程数设置到150,cpu还会被压榨的更加极限,但是一般不推荐一个进程使用的线程超过100,因为这样系统会耗费很多资源来管理线程。

 但是这的确和FME没啥太大的联系,如果我们用纯python写也能实现,而且python还可以开多进程,每个进程开n个线程池。这个案例只是为了让大家理解,如果是单要素执行,并且比较耗费时间的任务,我们完全可以用python开启多线程执行,我们以一个稍微复杂点的案例来说明。

这个模板为遥感影像特征提取模板,运行逻辑是读取栅格数据,然后将栅格数据分幅后将栅格数据转换为二进制流传递给深度学习模型,计算后得到影像特征矢量的模板。

主要执行以下几行代码,但是该代码是单线程,在数据量过大时就显得非常缓慢。

 

 耗时接近30s

接下来启用多线程

    def input(self, feature):
        self.aa.append(feature)

    def close(self):
        with ThreadPoolExecutor(50) as t:
            thread_mission_list = []
            for i in self.aa:
                run_thread=t.submit(self.deeplab.detect_image,feature=i)
                thread_mission_list.append(run_thread)
            index=0
            for mission in as_completed(thread_mission_list):
                r_image=mission.result()
                self.aa[index].setAttribute('_rasterBlob',r_image)
                self.pyoutput(self.aa[index])
                index+=1

 耗时17.3秒

 大概是因为数据量太小的原因,而且TensorFlow初始化模型和环境,需要大概5秒左右,如果数据量再多点的话,差距会拉开的更大。


总结

没有一成不变的方案,只有最优的选择。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

努力的无空

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值