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