【问题标题】:Fastest language for computationally intensive functions in OracleOracle 中用于计算密集型函数的最快语言
【发布时间】:2012-08-19 04:42:56
【问题描述】:

目前我们用 PL/SQL 编写了相当多的函数(普通 CDF、逆 CDF、Vasicek 和各种派生类),但它们非常慢。

我可以通过在工作站上流式传输数据来获得更好的性能,在该工作站上我已经用 C# 编码了一些东西,然后将结果批量插入回来。然而,这种方法使网络成为瓶颈,如果我可以通过在 Oracle DB 中拥有更快的功能来“将工厂放在木材所在的地方”,那就更好了。

我想看看如何通过使用 c(++) 或 Java(或您可能拥有的任何其他替代方案)对其进行编码来加快速度。这里有人有这方面的经验吗?希望你们当中有人尝试过所有方法,并能解释哪种方法总体上效果最好。

这里的额外复杂性是 IT 很忙,所以如果我想放弃使用数据库上的某些功能,我需要做一个可靠的案例。我不能在那个盒子上玩太多,否则我会那样做。

我们正在使用 Oracle Database 11g Enterprise Edition Release 11.2.0.2.0 - 64bit Production

提前致谢,

格特-简

编辑

这是一个函数的例子,它是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 函数比 Oracle 实现快 320 倍(!),我真的不明白这种差异来自哪里。任何高达 3-10 倍的东西似乎都是合理的。我真的觉得我在这里遗漏了一些东西。任何人?

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

【问题讨论】:

  • 你分析过 pl/sql 吗?如果是这样,它指向什么?
  • 其实没有,不知道存在。希望我有特权。我会回来的。
  • Google DBMS_PROFILER 作为开始
  • 我很想看看一个比某些 C# 程序执行得慢的存储过程(可以轻松访问数据)。并不是说它不会发生,但很可能 pl/sql 代码可以得到显着改进。您可以发布一个您尝试过的 pl/sql 类型的简单示例吗?
  • 谢谢,例如,贾斯汀笔记中的本机编译可以提供帮助,但我怀疑它会明显更快。你确定这个功能是性能瓶颈吗?你怎么称呼它? (我假设循环通过一些数据游标)。如果你说 C# pgm 提供了更好的性能,我怀疑它是因为这个函数的 .NET 实现。

标签: oracle


【解决方案1】:

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

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

【讨论】:

  • 嗨,我更新了问题,cume_dist 找到一组行的 cum 分布,我需要将概率(0-100%)转换为标准偏差(大约 -5-+5)并返回很多次,比如 Excel 中的 NORMDIST 和 NORMINV 函数。它们似乎不存在于 Oracle 中,因此我们编写了自己的。但这似乎很慢,我还不知道为什么。
  • 外部程序是我所想的,我希望有人能用一种方法取得好的结果。可能存在使一种方法比另一种更好的方法的陷阱或问题,但我找不到这些方法的性能。
  • 本机编译的投票答案。如果所有这些东西确实被解释了,那将很容易解释糟糕的性能。哇,没想到会这么简单。尚未对其进行测试,但这似乎很有可能解决它。
  • 嗯,到目前为止,对于原生理论,原生函数实际上比解释函数慢一点。根本没有得到这个。
  • 抱歉,原生编译毕竟不能加快速度。我用基准更新了示例。
【解决方案2】:

格特-简,

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

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

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

问候,
抢。

【讨论】:

  • 您知道是否使用任何外部程序(c、java、.Net)不会产生这种开销?将尝试从 PL/SQL 做整个事情。
  • 这取决于您如何使用这些程序。在 SQL 内部(如果可能,我不确定)上下文切换也将为每一行发生一次。从 PL/SQL 调用时,在预先加载集合中的所有行之后,您将不会有此开销。
  • 好的,谢谢你......所以如果我可以将我的工作分成小批量,那么我可以从 PL/SQL 引擎中运行它们并更快。需要做一些 30M 行,每行都有许多不同的场景等。希望尽可能精简的用户体验,但也许我可以将其包装在某种程序中。谢谢!
猜你喜欢
  • 1970-01-01
  • 2011-03-03
  • 1970-01-01
  • 2011-08-17
  • 1970-01-01
  • 1970-01-01
  • 2015-05-19
  • 2020-02-11
  • 2018-07-03
相关资源
最近更新 更多