【问题标题】:How do you handle memory limitations & performance improvements of a process.您如何处理进程的内存限制和性能改进。
【发布时间】:2012-02-29 07:32:22
【问题描述】:

这实际上包含 2 个问题。

1) 给您一个项目,结果证明该项目生成的代码不符合环境的内存限制。你能做些什么来解决这个问题?

2) 给您一个项目,结果证明它的执行速度比预期的要慢。你是怎么处理的?

我能想到的答案:

1) 尽可能使用 std 库,因为它们可能无论如何都会被加载,用函数模块化代码以避免重写可能节省堆栈空间的重复代码。

2)在必要时引入内联函数以减少函数查找开销,编译器优化可能是(如果不是我正在使用 volatile)?

请提供尽可能多的解决方案:)

【问题讨论】:

    标签: c


    【解决方案1】:

    给您一个项目,结果证明该项目生成的代码无法满足环境的内存限制。你能做些什么来解决这个问题?

    确定允许多少代码占用空间,然后应用各种 code refactoring methods 以减少当前占用空间以适应允许的大小。

    您得到了一个项目,结果证明它的执行速度比预期的要慢。你是怎么处理的?

    分析项目以查找性能瓶颈,在已识别的瓶颈中,识别在项目执行时间90% 运行的总代码中的20%,然后针对该代码进行优化。

    【讨论】:

    【解决方案2】:

    1) 根据需要动态加载和卸载模块。

    在 Windows 下,您可以使用 LoadLibraryGetProcAddress 而不是实际链接到 lib 文件。链接会导致 dll 即使未使用也会加载到内存中。使用LoadLibrary,您可以在需要时加载它,这样它就不会占用内存,然后在完成时将其卸载。

    对于 Linux,请查看 this link,“使用 libdl 动态加载和卸载共享库”部分。

    2) 个人资料,当然。除非面试官特别要求微优化技巧,否则这是正确答案。

    【讨论】:

    • Under Windows, you can use LoadLibrary and GetProcAddress instead of actually linking to a lib file. Linking causes the dll to load into memory even if not used. Linux 中的任何等价物?
    • @Thunderman 我发现了一些东西,可能会有所帮助。我编辑了我的答案。
    • @Luchigan Grigore 谢谢..它有帮助。
    【解决方案3】:

    1) 给您一个项目,结果证明该项目生成的代码不符合环境的内存限制。你能做些什么来解决这个问题?

    所问的问题是“代码”不适合......如果从字面上理解(即与堆栈、堆和静态/全局数据不同)意味着:

    1) 一些可执行代码需要缩减大小和/或 2) 需要减少同时加载的代码量,和/或 3) 需要解除限制。

    这个问题可能允许也可能不允许 3) - 检查限制。如果面试官鼓励,从那里开始,因为这很容易获胜:例如。您可以将 16 位进程重新编译为 32 位,或者为编译器/链接器指定不同的可执行布局以增加支持的可执行文件大小吗?您能否通过分配更多硬盘给交换空间来增加虚拟地址空间而不会遇到过多的交换?

    要 1) 减少代码大小,首先要对其进行测量:无论是作为总数还是基于每个函数。检查编译器开关以优化空间速度及其影响。在每个函数级别,关注最大的函数,以及许多类似函数加起来很多可执行代码的任何情况。此类函数可能由嵌套宏或某些代码生成工具生成。尝试将通用算法分解为一个接受使用函数指针或运行时切换的自定义的函数。

    阅读最有问题的函数,了解可能导致膨胀的原因,例如它们调用大量时间的潜在内联函数。虽然对于非常简单的操作,函数调用可能比内联代码稍差,但重复内联非平凡函数可能会导致任意更糟糕的代码膨胀。函数内部的预处理器宏扩展也是有效的内联。

    对于 2) 减少并发加载的代码,您可以使用动态加载的库(例如,dlopen/dlsym/dlclose 在 Linux/UNIX 上)。这使您可以显式控制将代码映射到进程地址空间的时间窗口。或者,您可以有效地序列化对部分代码的需求,例如首先运行一些一次性数据生成,为后续流程使用准备数据。或者,根据您的操作系统和限制,您可能能够在相同或不同的主机上同时运行部分程序,并让它们使用 IPC 机制(例如共享内存或网络)进行通信。

    当然,问题中可能无意中使用了“代码”,而真正的问题是堆耗尽,甚至堆栈耗尽 - 也有不同的技术可以解决这些问题,但不希望在猜测中偏离轨道。

    2) 给您一个项目,结果证明它的执行速度比预期的要慢。你是怎么处理的?

    首先,您需要确定缓慢的原因和类型:

    • 项目是否受 I/O 限制、CPU 限制、步骤之间的硬编码延迟(尤其是在没有预先制定出更具可扩展性、更强大的方法的屏幕抓取应用程序中)、调度延迟等..
    • 那么 - 这是什么类型的缓慢 - 延迟或吞吐量?

    如果项目受 I/O 限制,那么很大程度上取决于它所等待的设备。将磁性硬盘换成 SSD 驱动器、添加 RAM 或升级网卡,您可能会轻松取胜。您可能决定更改执行该类型 I/O 的方式,例如从未压缩数据移动到压缩数据,反之亦然,删除不必要的流刷新,在文件前面创建索引,以便您可以直接查找所需数据而不是进行暴力搜索,调整 Nagle 参数以权衡 TCP 延迟与吞吐量,使用多播而不是许多点对点网络连接,使用二进制而不是 XML 序列化或数据格式,优先使用内存映射将read()ing 数据循环到本地缓冲区。您也许可以重新考虑 I/O 是如何完成的,例如在磁盘数据中使用更有效和更直接的内存索引,使用高质量的散列函数和更稀疏的表,而不是对已排序的数据进行二进制搜索。这些概念(在基础计算科学中的内存等效项(数组与哈希表)中很熟悉)在涉及 I/O 时可能会产生更严重的后果。

    如果项目受 CPU 限制,则使用分析器查看哪些函数占用了大部分时间。在大型函数中手动添加更多 CPU 和/或经过时间跟踪,直到您了解原因。每个问题的解决方式可能会有很大差异,但需要检查一些通用的优化方法:

    • 算法效率(例如,避免不必要的 O(n^2) 和对模糊或任意大的 n 进行更糟糕的操作)
    • 不必要的数据复制
    • 当早期计算的结果可以很容易地保存和重复使用时(例如,编译正则表达式然后使用它们一次,尽管一直需要完全相同的正则表达式),而不是不必要地重新计算值
    • 由您的算法代码生成的物理内存页面错误(与巨大的值矩阵最相关)

    此外,调度可能是一个大问题。您可能会发现原子操作的性能优于互斥锁,或者需要更改进程优先级或 CPU 绑定,或者做出基本决定,例如使用线程与 select/poll 处理多个客户端连接。

    我能想到的答案: 1)尽可能使用std库,因为可能无论如何都会加载它们,用函数模块化代码以避免重写可能节省堆栈空间的重复代码。

    很公平。

    2)在必要时引入内联函数以减少函数查找开销,编译器优化可能是(如果不是我正在使用 volatile)?

    绝对值得一看。在我的测量中——跨多个操作系统/CPU/编译器等——内联普通函数往往比函数调用快一个数量级,但对于更大的函数,内联会导致代码膨胀——在资源严重受限的环境中——最终导致更多缓存丢失和交换等。所以,它不是 100% 黑白的。

    请提供尽可能多的解决方案:)

    好吧,那需要一整天的时间...... :-P

    【讨论】:

      【解决方案4】:
      1. 确保您已启用 COMDAT 折叠、DCE 和未使用的功能消除是必须。消除不必要的依赖,使用代码覆盖工具和重构以避免重复。运行内存分析器也会有所帮助,然后您可以发现最需要修复的区域。 大多数编译器还提供优化大小而不是速度的选项。

      2. 使用分析器,让您知道为什么它没有正确执行,而不仅仅是胡乱猜测。

      【讨论】:

        【解决方案5】:

        我要做的第一件事就是问“多少钱?” ——到底有多大?它有多慢?我要研究的第二件事是已经做了什么(如果有的话)来优化代码以达到目标。

        一方面,如果没有做任何事情来达到目标​​并且您已经完成了几个百分点,那么很有可能在编译器标志上进行一些相当简单的工作就足以完成这项工作。如果您的代码具有合理的可移植性,则另一种可能性是只使用不同的编译器(例如,如果您正在使用 Microsoft 的编译器为 Windows 进行开发,那么您很有可能只需通过切换就可以在大小和速度上获得几个百分点到英特尔的编译器)。

        另一方面,如果你太大了 15 倍,太慢了 10 倍,并且已经投入了大量工作来优化以尝试更接近目标,它很可能是目标无法合理实现,您需要与客户协商更改目标 - 删除一些功能,使用更快且具有更多存储空间的目标系统等。简单的事实是,除非一开始做的事情真的很糟糕,在保留相同功能的同时使代码小 15 倍和快 10 倍的机会不大。

        我要补充一点,您应该通常从一开始就确保防止后一种情况。如果您的目标是对 RAM、CPU 速度等具有合理硬限制的系统,您通常希望从一开始就对这些进行预算。如果您最终的预算表明您需要仅使用 10K 的 RAM 对一百万个数据点进行 FFT 并且仍然在 1 纳秒内完成,那么您可能根本不需要编写任何代码来确定它只是不会发生。如果尺寸和速度很重要,那么您永远不应该达到与要求严重脱节的成品。

        当然,移动目标也是可能的——如果你最初为台式机开发一些东西,但最后客户说:“糟糕,我们在外面留下了一句话——我们的意思是 Windows 电话 7"。同样,您必须讨论优先事项,但在这种情况下,可能是关于您是否可以移动发货日期并从(接近)开始重新开始开发。

        【讨论】:

          猜你喜欢
          • 2020-02-29
          • 2022-10-08
          • 2011-10-16
          • 2011-01-24
          • 2014-08-05
          • 1970-01-01
          • 1970-01-01
          • 2015-03-13
          • 2013-12-01
          相关资源
          最近更新 更多