Oracle中计算密集型函数的最快语言

时间:2012-08-23 15:46:50

标签: oracle

目前我们在PL / SQL中编写了很多函数(普通CDF,反CDF,Vasicek和各种衍生函数),但它们非常慢。

通过在C#中编写了一些内容的工作站上传输数据然后批量插入结果,我可以获得更好的性能。然而,这种方法使网络成为瓶颈,如果我可以通过在Oracle DB中具有更快的功能来“将木材放在木材上”,那将会好得多。

我想看看如何通过c(++)或Java(或任何其他替代方案)编写代码来加快速度。这里有没有人有这方面的经验?希望你们中的一个人尝试过所有方法,并且可以解释哪些方法总体上最好。

这里更复杂的是IT很忙,所以如果我想要放弃使用DB上的某些功能,我需要做一个坚实的案例。我不能在那个盒子上玩很多东西,否则我会这样做。

我们使用的是Oracle Database 11g企业版11.2.0.2.0版 - 64位生产

提前致谢,

格特 - 扬

修改

以下是函数的示例,即Normal CDF by Cody

这与cume_dist之间的区别在于cume_dist在一组行中查找分布。我只需要将概率转换为标准偏差并返回(很多次),例如Excel中的NORMDISTNORMINV函数。

    function stdnormal_cdf(u number) return number is
  z number;
  y Number;
  begin
    y:=abs(u);
    if y <= 0.6629126073623883041257915894732959743297 then
      z:=y * y;
      y:=u * ((((1.161110663653770e-002 * z + 3.951404679838207e-001) * z + 2.846603853776254e + 001) * z + 1.887426188426510e + 002) * z + 3.209377589138469e + 003)/((((1.767766952966369e-001 * z + 8.344316438579620) * z + 1.725514762600375e + 002) * z + 1.813893686502485e + 003) * z + .044716608901563e + 003);
      return 0.5  +  y ;
    else
      z:=exp(-y * y/2)/2;
      if y <= 5.65685424949238019520675489683879231428 then
        y:=y/1.41421356237309504880168872420969807857;
        y:=((((((((2.15311535474403846e-8 * y + 5.64188496988670089e-1) * y + 8.88314979438837594) * y + 6.61191906371416295e01) * y + 2.98635138197400131e02) * y + 8.81952221241769090e02) * y + 1.71204761263407058e03) * y + 2.05107837782607147e03) * y + 1.23033935479799725e03)/((((((((1.00000000000000000e00 * y + 1.57449261107098347e01) * y + 1.17693950891312499e02) * y + 5.37181101862009858e02) * y + 1.62138957456669019e03) * y + 3.29079923573345963e03) * y + 4.36261909014324716e03) * y + 3.43936767414372164e03) *  + 1.23033935480374942e03);
        y:=z * y;
      else
        z:=z * 1.41421356237309504880168872420969807857/y;
        y:=2/(y * y);
        y:=y * (((((1.63153871373020978e-2 * y + 3.05326634961232344e-1) * y + 3.60344899949804439e-1) * y + 1.25781726111229246e-1) * y + 1.60837851487422766e-2) * y + 6.58749161529837803e-4)/(((((y + 2.56852019228982242) * y + 1.87295284992346047) * y + 5.27905102951428412e-1) * y + 6.05183413124413191e-2) * y + 2.33520497626869185e-3);
        y:=z * (1/1.77245385102123321827450760252310431421-y);
      end if;

      if u < 0 then 
        return y;
      else 
        return 1-y;
      end if;    
    end if;  
  end;

编辑2

好的,这里是基准。测试表有100k行。 Oracle和F#之间的功能是相互直接的翻译,并给出相同的结果。

查询:

select 
    sum(get_rwa(approach, exposure_class_code, pd_r, lgd_r, ead_r, maturity_r, net_sale, rwf_r)) 
from functest
  • 解释:12.8秒
  • 原住民:13.2秒
  • .Net(F#):0.04秒。

这会使.Net函数320x(!)比Oracle实现更快,我真的不明白这种差异可能来自哪里。任何高达3-10倍的东西似乎都是合理的。我真的觉得我在这里错过了一些东西。任何人?

在F#中,我首先将100k行加载到List中。 (看起来很公平,只是将Oracle中的任何其他专栏总结成0.06秒,所以在两种情况下排除数据访问时间似乎都是合理的。将数据加载到列表中大约需要3秒,所以即使我包含时间它需要打开连接,执行和流过网络等,然后它仍然快4倍。)

2 个答案:

答案 0 :(得分:6)

Oracle支持define and call external procedures的功能。假设您可以将C / C ++ / C#应用程序编译为DLL / .so并将该库移动到数据库服务器,则可以将DLL的函数作为外部过程公开,然后从数据库中调用DLL的函数。由于一切都将在同一台机器上运行,因此网络不会成为瓶颈。当然,这意味着您的C / C ++ / C#代码将使用服务器的处理资源 - 这可能是也可能不是一件好事,这取决于服务器的CPU与工作站的比较强大以及服务器的其他内容做。

根据您在PL / SQL中对逻辑进行编码的确切方式,您可能还需要考虑利用Oracle的内置分析函数(如cume_dist)进行累积分布(我假设这是你的意思)通过“普通CDF”)或编写自己的分析功能。由于您的代码是计算密集型的,因此您也可以从native compilation中受益。当然,这假设您已经对代码进行了分析,并且没有明显的位置/方法来调整PL / SQL。

答案 1 :(得分:4)

格特 - 一月,

可能,时差是由于SQL引擎和PL / SQL引擎之间的上下文切换造成的。 functest中的100,000行中的每一行都通过PL / SQL例程get_rwa(和/或stdnormal_cdf)。上下文切换涉及保存状态和恢复状态,一旦完成,您可能不会注意到。但是这样做了10万次就可以了。

所以我建议在一个包含100,000行的嵌套表中加载100,000行,并将这个嵌套表只传递给一个PL / SQL例程,该例程执行一个简单的“for i in 1 .. [nested_table_variable] .count循环...结束循环;“,同时总结各个结果。

另一种选择是在SQL中完成所有操作,而无需使用PL / SQL。

的问候,
罗布。