为什么在使用图书馆时很难提高效率?

数据挖掘 大数据 效率 表现
2021-09-28 06:18:43

任何小型数据库处理都可以通过 Python/Perl/... 脚本轻松处理,这些脚本使用库和/甚至是语言本身的实用程序。然而,在性能方面,人们倾向于使用 C/C++/低级语言。根据需求定制代码的可能性似乎是这些语言对 BigData 如此有吸引力的原因——无论是关于内存管理、并行性、磁盘访问,甚至是低级优化(通过 C/C++ 级别的汇编结构)。

当然,这些好处并非没有成本:编写代码,有时甚至重新发明轮子,可能会非常昂贵/令人厌烦。尽管有很多可用的库,但人们倾向于在需要授予性能时自己编写代码。在处理大型数据库时,是什么禁用了使用库的性能断言?

例如,考虑一个不断爬取网页并解析收集到的数据的企业。对于每个滑动窗口,在提取的数据上运行不同的数据挖掘算法。为什么开发人员会放弃使用可用的库/框架(无论是用于爬网、文本处理和数据挖掘)?使用已经实现的东西不仅可以减轻整个过程的编码负担,还可以节省大量时间。

一口气

  • 是什么让自己编写代码成为性能的保证
  • 当您必须确保高性能时,为什么依赖框架/库会有风险?
4个回答

当性能成为问题时,我不认为每个人都会使用 C/C++。

编写低级代码的优点是使用更少的 CPU 周期,或者有时更少的内存。但我会注意到,高级语言可以调用低级语言,并这样做,以获得一些这种价值。Python 和 JVM 语言可以做到这一点。

例如,在她的桌面上使用 scikit-learn 的数据科学家已经在调用经过高度优化的本机例程来进行数字运算。编写新代码以提高速度是没有意义的。

在分布式“大数据”上下文中,您通常是数据移动的瓶颈:网络传输和 I/O。本机代码没有帮助。帮助的不是编写相同的代码以更快地运行,而是编写更智能的代码。

与 C/C++ 相比,高级语言将让您在给定的开发人员时间内实现更复杂的分布式算法。在规模上,具有更好数据移动的更智能算法将击败愚蠢的本机代码。

开发人员的时间和错误通常比新硬件成本更高,这也是事实。高级开发人员一年的时间可能满载 20 万美元;一年多的时间,还租用了数百台服务器的计算时间。在大多数情况下,为优化而不是投入更多硬件而烦恼可能是没有意义的。

我不明白关于“授予”、“禁用”和“断言”的后续行动?

我自己一遍又一遍地完成了重写游戏(并且仍在这样做),我的第一反应是适应性

虽然框架和库有大量(可能是相互交织的)用于标准任务的例程,但它们的框架属性通常(总是?)不允许使用快捷方式。事实上,大多数框架都有某种核心基础设施,围绕这些核心基础设施实现了基本功能的核心层。更具体的功能利用了基本层,并放置在核心周围的第二层中。

现在通过快捷方式,我的意思是直接从第二层例程到另一个第二层例程,而不使用核心。典型的例子(来自我的领域)是时间戳:你有某种时间戳数据源。到目前为止,工作只是从线路中读取数据并将其传递给核心,以便您的其他代码可以享用它。

现在,您的行业出于一个很好的理由更改了默认时间戳格式(在我的例子中,它们从 unix 时间变为 GPS 时间)。除非您的框架是特定于行业的,否则他们不太可能愿意更改时间的核心表示,因此您最终使用的框架几乎可以满足您的需求。每次访问数据时,都必须先将其转换为行业时间格式,并且每次要对其进行修改时,都必须将其转换回核心认为合适的任何格式。如果没有双重转换,您无法将数据直接从源传递到接收器。

这是您手工制作的框架将大放异彩的地方,这只是一个微小的变化,您将重新对现实世界进行建模,而所有其他(非行业特定)框架现在将具有性能劣势。

随着时间的推移,现实世界和模型之间的差异会越来越大。使用现成的框架,您很快就会面临以下问题:我如何代表thisthat如何使例行X接受/生产Y

到目前为止,这与 C/C++ 无关。但是,如果由于某种原因,您无法更改框架,即您必须忍受数据的双重转换才能从一端到另一端,那么您通常会采用一些可以最大限度地减少额外开销的东西。就我而言,TAI->UTC 或 UTC->TAI 转换器最好留给原始 C(或 FPGA)。没有优雅的可能,没有深刻的智能数据结构可以使问题变得微不足道。这只是一个无聊的 switch 语句,为什么不使用编译器擅长优化的语言呢?

众所周知,在数字世界中,有很多方法可以做同样的工作/获得预期的结果。

来自代码的责任/风险在开发人员的肩上。

这很小,但我想是 .NET 世界的一个非常有用的例子..

因此,许多 .NET 开发人员在其数据序列化上使用内置的 BinaryReader - BinaryWriter 以提高性能/控制流程。

这是 FrameWork 内置 BinaryWriter 类的 CSharp 源代码,是重载的写入方法之一:

// Writes a boolean to this stream. A single byte is written to the stream
// with the value 0 representing false or the value 1 representing true.
// 
public virtual void Write(bool value) 
{
     //_buffer is a byte array which declared in ctor / init codes of the class
    _buffer = ((byte) (value? 1:0));

    //OutStream is the stream instance which BinaryWriter Writes the value(s) into it.
    OutStream.WriteByte(_buffer[0]);
}

如您所见,此方法无需额外分配给 _buffer 变量即可编写:

public virtual void Write(bool value) 
{
    OutStream.WriteByte((byte) (value ? 1 : 0));
}

如果没有分配,我们可以获得几毫秒。这几毫秒可以接受为“几乎没有”但是如果有成千上万的写入(即在服务器进程中)怎么办?

让我们假设“少数”是 2(毫秒),而数千个实例只有 2.000.. 这意味着处理时间增加了 4 秒.. 4 秒后返回..

如果我们继续从 .NET 开始,并且如果您可以从 MSDN 检查 BCL - .NET 基类库 - 的源代码,您会看到开发人员决定的很多性能损失。

来自 BCL 源代码的任何观点 您看到开发人员决定使用 while() 或 foreach() 循环是很正常的,这可以在他们的代码中实现更快的 for() 循环。

这个小小的收益给了我们整体性能。

如果我们返回到 BinaryWriter.Write() 方法..

实际上,额外分配给 _buffer 实现并不是开发人员的错误。这正是决定“保持安全”!

假设我们决定不使用 _buffer 并决定实现第二种方法。如果我们尝试使用第二种方法通过线路发送数千字节(即上传/下载 BLOB 或 CLOB 数据),它通常会失败,因为连接丢失..因为我们尝试在没有任何检查和控制机制的情况下发送所有数据。当连接丢失时,服务器和客户端都不知道发送的数据是否完成。

如果开发人员决定“保持安全”,那么通常这意味着性能成本取决于实施的“保持安全”机制。

但是,如果开发人员决定“冒险,获得性能”,这也不是错误。直到有一些关于“冒险”编码的讨论。

并且作为一个小提示:商业库开发人员总是试图保持安全,因为他们不知道他们的代码将在哪里使用。

从程序员的角度来看,框架很少将性能作为最高优先级。如果你的库要被广泛使用,人们最看重的可能是易用性、灵活性和可靠性。

性能通常在二级竞争库中受到重视。“X 库更好,因为它更快。” 即便如此,这些库也经常会以最优化的解决方案换取可广泛利用的解决方案。

通过使用任何框架,您本质上就冒着存在更快解决方案的风险。我什至可以说几乎总是存在更快的解决方案。

自己写东西并不能保证性能,但如果你知道自己在做什么并且有相当有限的要求,它会有所帮助。

一个例子可能是 JSON 解析。有一百个用于各种语言的库,可以将 JSON 转换为可引用对象,反之亦然。我知道一种在 CPU 寄存器中完成所有工作的实现。它比所有其他解析器要快得多,但它也非常有限,并且该限制将根据您使用的 CPU 而有所不同。

构建特定于高性能环境的 JSON 解析器的任务是个好主意吗?我会在 100 次中使用一个受人尊敬的库 99 次。在那个单独的实例中,几个额外的 CPU 周期乘以一百万次迭代将使开发时间值得。