c++ - c++中的内存管理优化
问题描述
我正在尝试实现一系列转换。表示变换前后的对象分别是A类和B类,用于以最小化复杂度来演示示例。换句话说,A类可以转换为B类,B类可以逆变换为A类。此外,在A类和B类的实现中使用std::unique_ptr的数据容器被提取到类Base中。基类如下所示。
class Base
{
public:
Base() // Default constructor
{
this->size = 0;
}
~Base()
{
}
Base(int input_size, float input_value) // Constructor
{
this->size = input_size;
this->data = std::make_unique<float[]>(input_size);
for (int loop_number = 0; loop_number < size; loop_number++) {
data[loop_number] = input_value;
}
}
std::unique_ptr<float[]> get_data()
{
// deep copy
auto return_data = std::make_unique<float[]>(size);
for (int loop_number = 0; loop_number < size; loop_number++) {
return_data[loop_number] = data[loop_number];
}
return return_data;
}
int get_size()
{
return this->size;
}
protected:
int size;
std::unique_ptr<float[]> data;
};
接下来,类A和类B继承类Base。
class B;
class A : public Base
{
public:
A(int input_size, std::unique_ptr<int[]> const& input_data) // constructor
{
std::cout << "Object A " << std::to_address(this) << " constructed.\n"; // for observation
this->size = input_size;
this->data = std::make_unique<float[]>(this->size);
for (int loop_number = 0; loop_number < input_size; loop_number++)
{
this->data[loop_number] = input_data[loop_number]; // Deep copy
}
}
A& operator=(A const& InputImage) // Copy Assign
{
this->size = InputImage.size;
for (int loop_number = 0; loop_number < this->size; loop_number++)
{
this->data[loop_number] = InputImage.data[loop_number]; // Deep copy
}
return *this;
}
~A()
{
std::cout << "Object A " << std::to_address(this) << " destructed.\n"; // for observation
}
B to_B();
private:
int transform_to_B(int input_value)
{
return std::cos(input_value); // For example
}
};
class B : public Base
{
public:
B(int input_size, std::unique_ptr<int[]> const& input_data) // constructor
{
std::cout << "Object B " << std::to_address(this) << " constructed.\n"; // for observation
this->size = input_size;
this->data = std::make_unique<float[]>(this->size);
for (int loop_number = 0; loop_number < input_size; loop_number++)
{
this->data[loop_number] = input_data[loop_number]; // Deep copy
}
}
auto to_A()
{
std::unique_ptr<int[]> transformed_data = std::make_unique<int[]>(this->size);
for (int loop_number = 0; loop_number < this->size; loop_number++) {
transformed_data[loop_number] = transform_to_A(this->data[loop_number]);
}
return A(this->size, transformed_data);
}
~B()
{
std::cout << "Object B " << std::to_address(this) << " destructed.\n"; // for observation
}
private:
int transform_to_A(int input_value)
{
return std::acos(input_value); // For example
}
};
B A::to_B()
{
std::unique_ptr<int[]> transformed_data = std::make_unique<int[]>(this->size);
for (int loop_number = 0; loop_number < this->size; loop_number++) {
transformed_data[loop_number] = transform_to_B(this->data[loop_number]);
}
return B(this->size, transformed_data);
}
主要功能是测试A类和B类的转换结果。
int main()
{
const int size_for_testing = 3840 * 2160;
auto data_for_testing = std::make_unique<int[]>(size_for_testing);
for (int loop_number = 0; loop_number < size_for_testing; loop_number++) {
data_for_testing[loop_number] = 1; // for example
}
A a_object(size_for_testing, data_for_testing);
for (int loop_times = 0; loop_times < 1000; loop_times++) // for observation
{
// version 1
a_object = a_object.to_B().to_A().to_B().to_A().to_B().to_A();
}
return 0;
}
控制台输出是
Object A 00000038FC19FE28 constructed.
Object B 00000038FC19FE10 constructed.
Object A 00000038FC19FE00 constructed.
Object B 00000038FC19FDF0 constructed.
Object A 00000038FC19FDE0 constructed.
Object B 00000038FC19FDD0 constructed.
Object A 00000038FC19FDC0 constructed.
Object A 00000038FC19FDC0 destructed.
Object B 00000038FC19FDD0 destructed.
Object A 00000038FC19FDE0 destructed.
Object B 00000038FC19FDF0 destructed.
Object A 00000038FC19FE00 destructed.
Object B 00000038FC19FE10 destructed.
Object B 00000038FC19FE10 constructed.
Object A 00000038FC19FE00 constructed.
Object B 00000038FC19FDF0 constructed.
......
我知道为处理而创建的中期对象在 for 循环的范围结束时被释放是有道理的。但是,如果出现更复杂的情况,例如a_object = a_object.to_B().to_A().to_B().to_A().to_B().to_A().to_B().to_A().to_B().to_A().to_B().to_A();
,在保留这些中期对象时可能会导致巨大的内存消耗。我很好奇设计“在声明的范围结束时释放对象”的概念或哲学。它可能可以根据使用情况进行优化。
另一方面,如下所示的分离形式的内存使用与版本 1 相似。
// version 2
auto temp1 = a_object.to_B();
auto temp2 = temp1.to_A();
auto temp3 = temp2.to_B();
auto temp4 = temp3.to_A();
auto temp5 = temp4.to_B();
a_object = temp5.to_A();
为了尽量减少内存消耗,Lambda 表达式也被考虑如下。但是,内存使用也与版本 1 相似。
// version 3
auto temp1 = [](auto& input_object) { return input_object.to_B(); }(a_object);
auto temp2 = [](auto& input_object) { return input_object.to_A(); }(temp1);
auto temp3 = [](auto& input_object) { return input_object.to_B(); }(temp2);
auto temp4 = [](auto& input_object) { return input_object.to_A(); }(temp3);
auto temp5 = [](auto& input_object) { return input_object.to_B(); }(temp4);
auto a_object = [](auto& input_object) { return input_object.to_A(); }(temp5);
顺便说一句,这种 Lambda 表达式似乎无法合并如下。编译器弹出 C2664 错误并说 'auto main::::operator ()(_T1 &) const': cannot convert argument 1 from 'B' to '_T1 &'
a_object = [](auto& input_object) { return input_object.to_A(); }(
[](auto& input_object) { return input_object.to_B(); }(
[](auto& input_object) { return input_object.to_A(); }(
[](auto& input_object) { return input_object.to_B(); }(
[](auto& input_object) { return input_object.to_A(); }(
[](auto& input_object) { return input_object.to_B(); }(a_object))))));
最后,我的问题是:
1)我很好奇设计“在声明的范围结束时释放对象”的概念或哲学。它可能可以根据使用情况进行优化。
2)有没有更好的方法来减少这种级联结构的内存消耗,例如更复杂的情况a_object = a_object.to_B().to_A().to_B().to_A().to_B().to_A().to_B().to_A().to_B().to_A().to_B().to_A();
?
环境:
CPU:英特尔® 酷睿™ i7-6700HQ 2.6GHz
内存:16GB
操作系统:Windows 10 1909
IDE:Microsoft Visual Studio Community 2019 版本 16.4.5
解决方案
1)我很好奇设计“在声明的范围结束时释放对象”的概念或哲学。它可能可以根据使用情况进行优化。
它允许“安全”使用临时是该语句。
如果可观察行为相同,则 As-if 规则可能允许在之前解除分配。哪个更复杂,因为您在析构函数中有输出。
2)有没有更好的方法来减少这种级联结构的内存消耗,例如更复杂的情况 a_object = a_object.to_B().to_A().to_B().to_A().to_B().to_A( ).to_B().to_A().to_B().to_A().to_B().to_A();?
您可以为临时添加重载:
B A::to_B() &&
{
std::unique_ptr<int[]> transformed_data = std::make_unique<int[]>(this->size);
for (int loop_number = 0; loop_number < this->size; loop_number++) {
transformed_data[loop_number] = transform_to_B(this->data[loop_number]);
}
auto Bsize = this->size;
this->size = 0;
this->data.reset(); // We clear here.
// Implementation might even really transfer the buffer
return B(Bsize, std::move(transformed_data));
}
推荐阅读
- remote-server - 我有数据流到 PCIe 端口,如何最好地将其直接保存到通过 10GbE 端口连接的远程驱动器?
- python - ImageGenerator.flow_from_directory (class_mode) 的区别?
- javascript - 如果更改了所选选项,如何动态附加特定的 div?
- javascript - .replace() 内容可编辑的负正则表达式
- random - 具有固定种子和可变精度的 boost::multiprecision 随机数
- javascript - 为什么我的 javascript 不能重定向以下代码,我尝试了多种解决方案,我在 StackOverFlow 中找到了
- javascript - Express 路由器在应返回 Not Found (404) 时调用另一条路由
- azure-devops - Azure DevOps - 如何在不重新创建新管道的情况下更新管道存储库源?
- python - 如何在 Tornado 中编写非阻塞、分块的 RequestHandler
- sqlite - SQFLite:当字段值为 json_encode 时如何获取数据?