【问题标题】:Memory Efficiency Regarding The Use of Unions in C++关于在 C++ 中使用联合的内存效率
【发布时间】:2013-10-27 06:06:13
【问题描述】:

考虑下面的三个代码块,字符串/联合冲突的三个替代解决方案。

在这些选项中,一般来说,以这种方式使用联合的内存效率更高?

我在这里寻找解决原则的答案:也就是说,联合的主要目的是节省内存。

编辑:课堂作业迫使我以这种方式使用联合。它让我思考哪个是最有效的,这就是我到达这里的原因。

代码块(A):

// unions with pointers to structs
struct HourlyInfo {
    string firstName;
    string lastName;
    string title;
    int hoursWorked;
    double hourlyRate;
};

struct SalaryInfo {
    string firstName;
    string lastName;
    string title;
    double salary;
    double bonus;
};

struct Employee {
    bool isHourly;
    union {
        HourlyInfo *hourlyEmployee;
        SalaryInfo *salaryEmployee;
    }

};

代码块(B):

// applying unions to relevant and non-string data types
struct Employee {
    string firstName;
    string lastName;
    string title;
    bool isHourly;
    union {
        struct {
            double hourlyRate;
            int hoursWorked;
        } hourly;
        struct {
            double salary;
            double bonus;
        } salaried;
    };
};

代码块 (C):

// use cstring instead of string
struct HourlyInfo {
    cstring firstName[50];
    cstring lastName[50];
    string title[50];
    int hoursWorked;
    double hourlyRate;
};

struct SalaryInfo {
    cstring firstName[50];
    cstring lastName[50];
    cstring title[50];
    double salary;
    double bonus;
};

struct Employee {
    bool isHourly;
    union {
        HourlyInfo hourlyEmployee;
        SalaryInfo salaryEmployee;
    }
};

(注意:代码背后的想法是任何员工要么是小时工,要么是薪水,因此这里是工会。请不要为这个问题提出不涉及工会的替代解决方案。我不担心解决一个具体问题,我对工会感兴趣。)


此外,指针和其他数据类型的大小似乎差异很大:

What does the C++ standard state the size of int, long type to be?

How much memory does a C++ pointer use?

这是否意味着我们不能在这里就内存效率做出一概而论的陈述?如果是这样,在确定最有效的方法时我们应该考虑哪些因素?

【问题讨论】:

  • 您并没有真正进行公平的比较,因为 A 和 C 还需要一个 bool 来识别工会的哪个成员是活动的。
  • 在union之前你算过内存大小了吗?你知道 std::string 是如何工作的吗?为什么你有50个?什么是字符串? const char*?
  • 你的目标是什么?为什么不class Employee {...}; class HourlyEmployee : public Employee {...}; class SalaryEmployee : public Employee {...} 等?
  • “请不要为这个问题提出不涉及联合的替代解决方案”——联合是一个问题,而不是一个解决方案。
  • @sehe 好点。我会尝试在问题中更清楚地说明这一点。这确实是出于好奇。

标签: c++ pointers memory unions


【解决方案1】:

规则 #1:遵循您的分析器(它会告诉您哪个对您的程序更有效)

规则 #2:关于内存分配:使用自定义分配器为您隐藏复杂性

规则 #3:设计数据类型以明确表达意图/目的(从这个意义上说,只有 B 是一个选项)。当然,除非第 1 条规则需要另外采取(这很不寻常)

我知道我“不允许”提出替代方案:(Live on Coliru)

#include <string>
#include <boost/variant.hpp>

struct HourlyInfo {
    int    hoursWorked;
    double hourlyRate;
};

struct SalaryInfo {
    double salary;
    double bonus;
};

namespace detail {

    template <template <typename...> class Allocator = std::allocator>
    struct basic_employee {
        using string = std::basic_string<char, std::char_traits<char>, Allocator<char>>;
        string firstName;
        string lastName;
        string title;

        using RateInfo = boost::variant<HourlyInfo, SalaryInfo>;
        RateInfo rates;
    };
}

using Employee = detail::basic_employee<>; // or use a custom (pool?) allocator

int main()
{
    Employee staff1 = { 
        "John", "Cage", "From accounting", 
        SalaryInfo { 1900.00, 120.0 } 
    };
    Employee contractor = { 
        "Joe", "Duffy", "Plumbing jobs", 
        HourlyInfo { 3, 46.00 } 
    };
}

【讨论】:

  • 我的猫对 Boost 过敏,所以这里不能用 ;-)
  • @sunday 然后接受治疗^H^Ha 新猫。 Boost 对于 C++ 来说是必须的。,
  • @sunday 为将其编辑回更有趣的形式而欢呼:/
  • @sehe 同意了。帮助其他人意识到我在开玩笑:) 顺便说一句,我正在更新问题。整个事情的发生是因为课堂作业迫使我使用联合,我很好奇哪种方法更好。
  • @sunday 是的,我想,因此我的comment at the OP
【解决方案2】:

B 将可能使用最少的内存,但 50 是一个不错的选择,因为它让人怀疑。

对于 A,大概您要对两种可能性中的一种进行单独的内存分配。在内存使用方面,这几乎总是会引入一些低效率,加上指针本身的空间,所以它输给了 B。不过,在两个信息结构的大小差异更大的特殊情况下,它可能会获胜比指针的大小,超过一定比例使用较小的结构,并且您从 猛烈 低开销的内存分配器(例如池分配器)分配它们。

对于 C,我假设您的意思是 50 个 char 的数组,而不是 50 个 stringcstring 的数组。我相信名称的平均长度加上string 的开销小于 50 个字符,这就是我说 B 优于 C 的基础。但是,string 的开销取决于关于一些实现细节,所以我不能断然地说。此外,如果您要与名字都略低于 50 个字符的人打交道,那么 C 会赢。我只是认为这不太可能。

当然,C 的限制更大,因为它根本无法处理名字超过 50 个字符的人(如果存储以 nul 结尾的字符串,则为 49 个字符)。

[编辑:再想一想,字符串的开销可能是:

  • 8 字节的起始指针
  • 8 字节的结束指针
  • 8 字节的容量
  • 另外两个指针(16 字节)用于包含字符串数据的分配标头,分配本身向上舍入为 8 或 16 字节。

一个短字符串总共需要 48 或 56 个字节(尽管有一种叫做“短字符串优化”的东西可以让短字符串变得更好,尽管根据细节它可能会使长字符串变得更糟)。有了string 的实现和内存分配,C 将获胜,即使没有四舍五入,它也可能在斯里兰卡获胜。

因此,值得研究如何衡量实际内存使用情况。]

【讨论】:

  • 请记住,字符数组的名称分为第一个和最后一个。我认为大多数名称将在 50 个字符以下。这是否违背“C 会赢。我只是认为这不太可能。” ?
  • 另外,您在代码块 (C) 中的 s/string/cstring 假设上是正确的。
  • @sunday:确实,如果名称都略低于 50 个字符,那么 C 将获胜。同样,我认为这些名称不太可能都略低于 50 个字符。如果我们将“slightly”的值作为字符串的开销,那么我们可以说重要的是平均名称长度是大于还是小于50 - slightly。这适用于“名称”是否意味着全名,或者在这种情况下,分别考虑三个部分中的每一个。如果是全名,那我就不那么自信了。字符串的开销很容易超过 16 个字节 ...
  • ...我的全名很短,但仍然是 19 个字符,包括两个空格。
  • 我对你的使用略有误解,但我正在关注你。感谢您的回答不断扩展。我不知道为什么人们不赞成你的,因为它实际上在这里解决了这个问题。
猜你喜欢
  • 2012-01-11
  • 2015-06-30
  • 2021-12-03
  • 2013-09-11
  • 1970-01-01
  • 2011-08-18
  • 1970-01-01
  • 2021-07-19
  • 1970-01-01
相关资源
最近更新 更多