怎样缩短c语言的占内存大小,关于优化:减少C语言中的内存使用的最佳实践是什么?...

什么是"高效内存C编程"的最佳实践?

通常对于嵌入式/移动设备,低内存消耗的准则应该是什么?

我猜应该为a)代码存储b)数据存储单独制定准则

很高兴看到这么多截然不同的好答案,而不是重复。

让我完善您的头衔。"内存有效"还可以表示读取和/或写入速度。

在C语言中,从简单得多的角度考虑以下内容;

使用#pragma pack(1)字节对齐结构

在结构可以包含不同类型的数据的地方使用联合

使用位字段而不是整数来存储标志和小整数

避免使用固定长度的字符数组来存储字符串,实现字符串池和使用指针。

在存储对枚举字符串列表的引用的地方,例如字体名称,将索引而不是字符串存储到列表中

使用动态内存分配时,请预先计算所需的元素数量,以避免重新分配。

啊,我自己完全忘记了前三点。我认为它太明显了,因此忽略了它。 xD

是的阅读完您的书后,便增加了以上答案,并添加了所有更明显的遗漏。

1.这可能会带来速度损失。更好:智能地构造您的结构:struct s {int i;字符c; }而不是struct s {char c; int i;} 2.工会非常好。 3.位域可能会非常缓慢。谨防

@Mikeage-是的,我发现通常是针对内存或速度进行优化,而两者是相对的。

大多数嵌入式系统都有位操作指令,因此位域是一个不错的选择

我发现一些对嵌入式系统有用的建议:

确保使用const实际声明了任何查找表或其他常量数据。如果使用const,则数据可以存储在只读(例如闪存或EEPROM)存储器中,否则在启动时必须将数据复制到RAM,这会占用闪存和RAM空间。设置链接器选项,以使其生成映射文件,并研究该文件以查看数据在内存映射中的确切分配位置。

确保您正在使用所有可用的内存区域。例如,微控制器通常具有您可以利用的板载内存(与外部RAM相比,其访问速度也可能更快)。您应该能够使用编译器和链接器选项设置来控制将代码和数据分配到的内存区域。

要减小代码大小,请检查编译器的优化设置。大多数编译器都具有用于优化速度或代码大小的开关。可以尝试使用这些选项来查看是否可以减小已编译代码的大小。显然,应尽可能消除重复的代码。

检查系统需要多少堆栈内存,并相应地调整链接器内存分配(请参阅此问题的答案)。为减少堆栈使用量,请避免在堆栈上放置大型数据结构(无论与您有关的"大"值如何)。

确保尽可能使用定点/整数数学。当简单的按比例缩放的整数数学就足够了时,许多开发人员都会使用浮点数学(以及缓慢的性能以及庞大的库和内存使用)。

假设系统缺少FPU,并且需要模拟浮点运算。尽管如此,浮点数和双精度型在大多数情况下仍比定点整数占用更多的内存,因此使用定点整数确实有所帮助。

大多数系统缺少FPU。整数规则!

所有好的建议。以下是一些我发现有用的设计方法。

字节编码

为专用字节码指令集编写一个解释器,并在该指令集中编写尽可能多的程序。如果某些操作需要高性能,请使其成为本机代码,然后从解释器调用它们。

代码生成

如果部分输入数据很少更改,则可以使用外部代码生成器来创建临时程序。这将比更通用的程序小,并且运行速度更快,并且不必为很少更改的输入分配存储空间。

讨厌数据

如果愿意让您存储绝对最小的数据结构,则愿意浪费很多周期。通常,您会发现性能受到的影响很小。

通过使用自己的内存分配器(或小心使用系统的分配器)避免内存碎片。

一种方法是对不同大小的对象使用"平板分配器"(例如,请参见本文)和多个内存池。

+1。当我们需要在8mb RAM中运行完整的HTML 4浏览器时,这是一个成功的因素。

您很可能需要仔细选择算法。针对具有O(1)或O(log n)内存使用量(即低)的算法。例如,在大多数情况下,连续可调整大小的数组(例如std::vector)所需的内存少于链接列表。

有时,使用查询表可能对代码大小和速度都更有利。如果在LUT中仅需要64个条目,那么与大型sin / cos / tan函数相比,sin / cos / tan(使用对称性!)为16 * 4字节。

压缩有时会有所帮助。顺序读取时,像RLE这样的简单算法很容易压缩/解压缩。

如果要处理图形或音频,请考虑使用其他格式。调色板或位打包*图形可能是质量的良好折衷,并且调色板可以在许多图像之间共享,从而进一步减小了数据大小。音频可以从16位减少到8位甚至4位,并且立体声可以转换为单声道。采样率可以从44.1KHz降低到22kHz或11kHz。这些音频转换极大地减少了数据大小(以及可悲的是,质量),而且微不足道(重采样除外,但这就是音频软件的用途==)。

*我想您可以将其压缩。图形的位打包通常是指减少每个通道的位数,以便每个像素可以容纳两个字节(例如RGB565或ARGB155)或原来三个或四个(分别为RGB888或ARGB8888)的一个字节(ARGB232或RGB332)。

预先预先分配所有内存(即,除了启动初始化外,没有任何malloc调用)对于确定性使用内存绝对有帮助。否则,不同的体系结构会提供帮助的技术。例如,某些ARM处理器提供了另一种指令集(Thumb),该指令集通过使用16位指令而不是正常的32位几乎减少了代码大小。当然,这样做会牺牲速度。

对于复杂的算法,ARM模式可以减小代码大小。对于简单的操作,THUMB模式可能更好。实际上,这完全取决于上下文。但是对于通用代码,我同意THUMB比ARM小。

对于某些应用程序的速度和大小,预分配效果很好。对于其他应用程序,这可能会加剧对大小的要求,因为该应用程序通常无法跨模块重复使用内存。

当字节到达时,可以对流执行某些解析操作,而不是复制到缓冲区并进行解析。

一些例子:

使用状态机解析NMEA蒸汽,仅将所需字段收集到效率更高的结构中。

使用SAX而不是DOM解析XML。

+1避免DOM。甚至在具有大量序列化数据的桌面应用程序上也是如此。

避免使用文本,请使用二进制。 XML比文本还差。我们的前辈提出ASN.1之类的原因是有原因的,并且它不易使用。

1)在开始项目之前,请构建一种可以衡量正在使用多少内存的方式,最好以每个组件为基础。这样,每次进行更改时,您都可以看到其对内存使用的影响。您无法优化无法衡量的内容。

2)如果项目已经成熟并且达到内存限制,(或已移植到内存较少的设备上),请找出您正在使用的内存。

我的经验是,修复超大应用程序时,几乎所有的重大优化都来自少量更改:减小缓存大小,删除一些纹理(当然,这是一项功能更改,需要利益相关者达成一致,即开会,因此在您的时间方面效率不高),重新采样音频,减少自定义分配的堆的前期大小,找到释放仅临时使用的资源并在需要时重新加载的方法。有时,您会发现一些结构,该结构为64个字节,可以减少为16个字节或其他内容,但这很少是挂得最低的果实。但是,如果您知道应用程序中最大的列表和数组是什么,那么您就会知道首先要看的结构。

哦,是的:查找并修复内存泄漏。您可以在不牺牲性能的情况下恢复的任何内存都是一个不错的开始。

过去,我花了很多时间来担心代码的大小。主要注意事项(除了:确保在构建时对其进行度量,以便可以看到它的变化)是:

1)找出引用了什么代码,引用了什么。如果您发现整个XML库仅链接到您的应用程序以解析两个元素的配置文件,请考虑更改配置文件格式和/或编写自己的琐碎解析器。如果可以,请使用源分析或二进制分析来绘制一个大的依赖关系图,并只用少量的用户来查找大型组件:可能只需要少量的代码重写就可以将它们剪裁掉。准备扮演外交官的角色:如果您的应用程序中两个不同的组件都使用XML,并且您想削减它,那么这两个人就必须让您相信手动滚动当前已受信任的现成库的好处。

2)混乱的编译器选项。请查阅您特定于平台的文档。例如,由于内联,您可能希望减少默认的可接受代码大小增加,并且至少在GCC上,您可以告诉编译器仅应用通常不会增加代码大小的优化。

3)尽可能利用目标平台上已经存在的库,即使这意味着编写适配器层。在上面的XML示例中,您可能会始终在目标平台上发现内存中始终存在一个XML库,因为OS会使用它,在这种情况下会动态链接到它。

4)正如其他人所述,拇指模式可以在ARM上提供帮助。如果仅将其用于对性能要求不高的代码,而将关键例程保留在ARM中,则您不会注意到其中的区别。

最后,如果您对设备有足够的控制权,则可以玩一些巧妙的技巧。用户界面一次只允许一个应用程序运行?卸载您的应用不需要的所有驱动程序和服务。屏幕是双缓冲的,但是您的应用已同步到刷新周期了吗?您也许可以收回整个屏幕缓冲区。

推荐这本书"小型内存软件:内存有限的系统的模式"

减少长度并消除尽可能多的字符串常量,以减少代码空间

仔细考虑算法与查找表之间的权衡

请注意如何分配不同种类的变量。

常量可能在代码空间中。

静态变量可能位于固定的内存位置。如果可能,请避免使用这些方法。

参数可能存储在堆栈或寄存器中。

局部变量也可以从堆栈中分配。如果在最坏的情况下代码可能会耗尽堆栈空间,请不要声明较大的本地数组或字符串。

您可能没有堆-可能没有操作系统来为您管理堆。可以接受吗?您是否需要一个malloc()函数?

除了其他建议外,请记住,在函数中声明的局部变量通常将分配在堆栈上。

如果堆栈内存有限,或者您想减小堆栈大小以为更多的堆或全局RAM腾出空间,请考虑以下事项:

展平调用树以减少在任何给定时间堆栈上的变量数量。

将较大的局部变量转换为全局变量(减少使用的堆栈数量,但增加使用的全局RAM数量)。可以声明变量:

全局范围:对整个程序可见

在文件范围内为静态:在同一文件中可见

在功能范围内静态:在功能范围内可见

注意:无论如何,如果进行了这些更改,如果您具有preemptive环境,则必须警惕reentrant代码的问题。

许多嵌入式系统没有堆栈监视器诊断程序来确保捕获堆栈溢出,因此需要进行一些分析。

PS:适当使用堆栈溢出的奖励吗?

我有一个嵌入式系统大会的主题演讲。它是从2001年开始的,但仍然非常适用。见论文。

另外,如果您可以选择目标设备的体系结构,则可以使用带有Thumb V2的现代ARM或带VLE的PowerPC或带MIPS16的MIPS,或者选择诸如Infineon TriCore或SH系列之类的紧凑型目标。很好的选择。更不用说紧凑的NEC V850E系列了。或者转到具有出色代码紧凑性的AVR(但它是8位计算机)。除了固定长度的32位RISC,其他任何方法都是不错的选择!

限制内存需求的一种好方法是尽可能多地依赖libc或其他可以动态链接的标准库。您必须在项目中包括的每个其他DLL或共享对象都是很大的内存,您可以避免刻录。

此外,在适用的情况下,利用联合和位字段,仅将程序正在处理的部分数据加载到内存中,并确保使用-Os(在gcc;或与编译器等效的开关)中进行编译针对程序大小进行优化。

在应用程序中有用的一个技巧是创建一个雨天的内存资金。在启动时分配足够大的单个块,足以执行清理任务。如果malloc / new失败,请释放雨天资金,并发布一条消息,让用户知道资源紧张,应该尽快保存。这是1990年左右在许多Mac应用程序中使用的技术。

  • 1
    点赞
  • 13
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值