使用PyCuda在Python中的嵌套for循环上进行并行编程(还是别的?)

2024-10-01 17:32:06 发布

您现在位置:Python中文网/ 问答频道 /正文

python函数的一部分如下所示:

for i in range(0, len(longitude_aq)):
    center = Coordinates(latitude_aq[i], longitude_aq[i])
    currentAq = aq[i, :]
    for j in range(0, len(longitude_meo)):
        currentMeo = meo[j, :]
        grid_point = Coordinates(latitude_meo[j], longitude_meo[j])
        if is_in_circle(center, RADIUS, grid_point):
            if currentAq[TIME_AQ] == currentMeo[TIME_MEO]:
                humidity += currentMeo[HUMIDITY_MEO]
                pressure += currentMeo[PRESSURE_MEO]
                temperature += currentMeo[TEMPERATURE_MEO]
                wind_speed += currentMeo[WIND_SPEED_MEO]
                wind_direction += currentMeo[WIND_DIRECTION_MEO]
                count += 1.0

    if count != 0.0:
        final_tmp[i, HUMIDITY_FINAL] = humidity/count
        final_tmp[i, PRESSURE_FINAL] = pressure/count
        final_tmp[i, TEMPERATURE_FINAL] = temperature/count
        final_tmp[i, WIND_SPEED_FINAL] = wind_speed/count
        final_tmp[i, WIND_DIRECTION_FINAL] = wind_direction/count

    humidity, pressure, temperature, wind_speed, wind_direction, count = 0.0, 0.0, 0.0, 0.0, 0.0, 0.0

final.loc[:, :] = final_tmp[:, :]

问题:len(longitude_aq)约为320k,len(longitude_meo)为700万。这使得代码的迭代次数接近21000亿次。。。在

我必须迭代一个文件(即longitude_aq文件),然后计算一些平均值,在第二个文件(即longitude_meo文件)上迭代,给出从第一个文件中提取的一些特征。在

看来我也不能有什么不同。在

可能的解决方案:并行编程。我的大学允许我接触他们的高性能计算机。它们有几个节点和几个可访问的gpu(对于gpu,列出here,对于cpu,here

目标:由于没有使用Python进行CUDA编程的经验,我想知道什么是将我的代码转换为HPC可运行的代码的最简单方法,从而大大减少了计算时间。在


Tags: 文件inlenifcounttmpfinalwind
1条回答
网友
1楼 · 发布于 2024-10-01 17:32:06

抱歉,阅读可能很难阅读,但现实是残酷的,许多狂热者可能很容易破坏“编码”的努力,变成一场主要是先验的失败战争。在盲目地将一个人*天的时间花在一个主要错误的方向之前,最好仔细地重新评估任何重新设计计划的所有先验已知的利弊。

如果我没有接触到一个项目,最高级别的院士已经花费了几十个man*years,是的,超过了与一个12岁以上的团队合作一年,如果使用适当的(硬件性能非破坏性)设计方法,则“生产”一种处理方法,其可在中复制,低于~ 15 [min](并且HPC/GPU基础设施成本更便宜)。。。

It doesn't seem like I can proceed any differently.

好吧,实际上很难说,如果不是不可能的话:

你的文章似乎假设了一些基本的东西,这些东西很可能会避免从将上述粗略的想法转移到一个真正专业的HPC/GPU基础架构中获得任何真正的好处。在

Possible solution: parallel programming

一种说/打字比实际操作更容易的方法

如果您的代码应该比纯代码执行流(如上所述)得到任何显著的改善,那么进程调度仍然只是一个愿望(相信我,或者Gene Amdahl,或者其他C/S老手)确实需要进行艰难的流程重新设计(如上所述)

1)纯粹的[SERIAL]本质的fileIO可以(几乎)杀死游戏:

关于pure-[SERIAL]文件的未发布部分访问(两个带有数据点的文件)。。。任何一个fileIO本质上都是最昂贵的资源,除了智能的重新设计之外,它是一个纯粹的-[SERIAL](最多是一站式的成本,但仍然)(重新)以顺序的方式读取,所以不要期望在重新设计的代码中有任何巨大的飞跃。这将永远是最慢和最昂贵的阶段。在

BONUS:
While this may seem as the least sexy item in the inventory list of the , , , , or whatever the slang brings next, the rudimentary truth is that making an HPC-computing indeed fast and resources efficient, both the inputs ( yes, the static files ) and the computing strategy are typically optimised for stream-processing and to best also enjoy a non-broken ( collision avoided ) data-locality, if peak performance is to get achieved. Any inefficiency in these two domains can, not just add-on, but actually FACTOR the computing expenses ( so DIVIDE performance ) and differences may easily get into several orders of magnitude ( from [ns] -> [us] -> [ms] -> [s] -> [min] -> [hr] -> [day] -> [week], you name them all ... )

2)成本/收益可能会让你付出更多

这一部分确实是你最大的敌人:如果你的一次总努力高于净收益的总和,那么GPU将不会增加任何附加值,或者根本不足以支付你的附加成本。在

为什么?

GPU引擎是SIMD设备,非常适合在重复的SMX指令块的大片区域内使用延迟掩蔽,这需要在本地进行某种“权重”—或“漂亮”—数学运算,如果他们要比其他问题实现策略——GPU设备(不是游戏玩家的设备,而是HPC设备,不是类中所有的卡都是,对吧?)为数据局部性较小的区域提供最佳服务(微内核矩阵操作,在2018/Q2具有非常密集、最好非常小的SMX local“RAM”占用空间,这种密集的内核<< ~ 100 [kB]占用空间很大)。在

你的“计算”部分代码对从原始静态存储中(相当昂贵)获取的任何单个数据元素都没有重复使用,因此几乎所有的好处,GPU/SMX/SIMD炮兵的发明根本就不被使用,而且尝试将这类代码加载到这种异构(NUMA复杂)分布式计算上会给您带来负面的净收益(没错,每个GPU设备都是相当“远的”,“昂贵”(除非你的代码会利用它的SMX资源,直到GPU的硅几乎冒出烟雾…)以及“远程”异步操作的分布式计算节点,在您的全局计算策略)系统中。在

GPU代码的任何第一次分支都将在SIMD执行成本上造成毁灭性的代价,因此您的严重if-ed代码在语法上是公平的,但是性能方面几乎是游戏的杀手:

for i in range( 0,
                len( longitude_aq )
                ): #______________________________________ITERATOR #1 ( SEQ-of-I-s )
    currentAq =                        aq[i, :]           # .SET
    center    = Coordinates(  latitude_aq[i],             # .SET
                             longitude_aq[i]
                             ) #          |
    #                                     +      -> # EASY2VECTORISE in [i]

    for j in range( 0,
                    len( longitude_meo )
                    ): #- - - - - - - - - - - - - - - - - ITERATOR #2 ( SEQ-of-J-s )
        currentMeo =                        meo[j, :]     # .SET
        grid_point = Coordinates(  latitude_meo[j],       # .SET
                                  longitude_meo[j]
                                  ) #           |
        #                                       +   -> # EASY2VECTORISE in [j]
        if is_in_circle( center,
                         RADIUS,
                         grid_point
                         ): # /\/\/\/\/\/\/\/\/\/\/\/\/\/ IF-ed SIMD-KILLER #1
            if (   currentAq[TIME_AQ]
               == currentMeo[TIME_MEO]
                  ): # /\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\ IF-ed SIMD-KILLER #2
                humidity       += currentMeo[      HUMIDITY_MEO] # BEST PERF.
                pressure       += currentMeo[      PRESSURE_MEO] #   IF SMART
                temperature    += currentMeo[   TEMPERATURE_MEO] #   CURATED
                wind_speed     += currentMeo[    WIND_SPEED_MEO] #   AS NON
                wind_direction += currentMeo[WIND_DIRECTION_MEO] #   ATOMICS
                count          += 1.0

    if          count          != 0.0: # !!!!!!!!!!!!!!!!!! THIS NEVER HAPPENS
        # EXCEPT WHEN ZERO DATA-POINTS WERE AVAILABLE FOR THE i-TH ZONE,
        #        FILE DID NOT CONTAIN ANY SUCH,
        #        WHICH IS FAIR,
        #    BUT SUCH A BLOCK OUGHT NEVER HAVE STARTED ANY COMPUTING AT ALL
        #     IF ASPIRING FOR INDEED BEING LOADED
        #        ONTO AN HPC-GRADE COMPUTING INFRASTRUCTURE ( SPONSORED OR NOT )
        #
        final_tmp[i, HUMIDITY_FINAL]       = humidity       / count
        final_tmp[i, PRESSURE_FINAL]       = pressure       / count
        final_tmp[i, TEMPERATURE_FINAL]    = temperature    / count
        final_tmp[i, WIND_SPEED_FINAL]     = wind_speed     / count
        final_tmp[i, WIND_DIRECTION_FINAL] = wind_direction / count

如果所有的运算{c>都能避免{在不到几个[ns]的情况下,可以非常便宜地相互独立地操作(最好使用预先获取的常量)[ns]是的,您的计算负载不需要任何东西,只需要几个单元[ns]来执行。

问题在于数据流的智能工程(我喜欢称之为data-HYDRAULICS(如何使更多不可压缩的数据流进入{ CPU | GPU | APU | *** }-处理器寄存器,从而获得em进程))

剩下的一切都很容易。HPC级数据液压系统的智能解决方案通常不是。在

没有语言,没有框架会自动帮助你。有些可以从您的“手动”工作中释放解决方案工程的某些部分,有些则不能,有些甚至会破坏可能的计算性能,这是由于内部设计决策中的“廉价”快捷方式和做出的妥协,而这些不利于同一目标-性能。在


最好的下一步?在

A)试着更好地了解您希望用于广泛(但不是密集型)的计算基础设施的限制(是的,每[i,j]只有几个SLOC),而HPC主管不希望看到这些基础设施流入其运营的昂贵的HPC资源中)。在

B)如果在时间+员工人数+财务资源方面遇到困难,无法重新设计自顶向下的数据液压解决方案,最好重新考虑您的代码,以便至少进入矢量化,numpy/numba(并不总是numba比已经智能化的numpy向量化代码走得更远,但是定量测试会告诉每个事件的事实,而不是一般情况)

C)如果您的计算问题预计会更频繁地重新运行,那么一定要从数据存储的早期预处理(处理过程中最慢的部分)开始评估重新设计的管道,在这种情况下,可以对主要静态值进行基于流的预处理,这可能会对产生的数据水力学流量(性能)产生最大的影响,其中预先计算的+智能对齐的值最大。如前所述,车道上几个ADD-s的街区不会得到改善,超过几个[ns],但慢流可以更快地跳跃数量级,如果重新安排成一个智能流,利用所有可用的,然而,“仅仅是”-[CONCURRENT]-ly操作的资源(任何试图安排一个真正的-[PARALLEL]调度的尝试在这里都是一派胡言,因为任务主要不是[PARALLEL]调度问题,而是对数据点进行纯粹的[SERIAL](再)处理,其中一个聪明的,然而,“仅仅”-[CONCURRENT]处理重组可能有助于缩小过程的持续时间)。在

BONUS:
If interested in deeper reasoning about the achievable performance gains from going into N-CPU operated computing graphs, feel free to learn more about re-formulated Amdahl's Law and related issues, as posted in further details here.

相关问题 更多 >

    热门问题