我正在为我的大学课程编写一个程序。它是2个处理器上简单版本的调度任务的动态编程算法的实现。因为它是一种记忆浪费的方法,我想到了它的一些改进。例如,一个不必存储整个S x n矩形阵列,其中S是所有任务的时间总和,n是任务数。因为在算法数据的第一次迭代中,只存储在n轴的小索引值中,我想我可以使我的数组成为一个三角形,即每个下一个子数组都是一定数量的元素。
然后我查看了任务管理器的内存使用情况,我很震惊。矩形阵列版本需要980KB。带有三角形阵列的版本(所以较小的一个)花费了近15MB!也许我不知道系统使用的内存分配方式,或者我有妄想。或者我在代码中犯了一些愚蠢的错误。但我敢打赌,我不知道什么。有人能让我高兴吗?
这是我的代码:
#include <iostream>
#include <fstream>
#include <conio.h>
#include <stack>
using namespace std;
void readTasks(char* filename, int*& outTaskArray, int& outTaskCount)
{
ifstream input = ifstream();
input.open(filename, ios::in);
input >> outTaskCount;
outTaskArray = new int[outTaskCount];
for (int i = 0; i < outTaskCount; ++i)
{
input >> outTaskArray[i];
}
input.close();
}
void coutTasks(int* taskArray, int taskCount)
{
cout << taskCount << " tasks:\n";
for (int i = 0; i < taskCount; ++i)
{
cout << i << ": " << taskArray[i] << endl;
}
}
void Scheduling2(int* taskArray, int taskCount, int memorySaving,
stack<int>*& outP1Stack, stack<int>*& outP2Stack)
{
bool** scheduleArray = new bool*[taskCount];
int sum;
// I know that construction below is ugly cause of code repetition.
// But I'm rather looking for performance, so I try to avoid e.g.
// checking the same condition too many times.
if (memorySaving == 0)
{
sum = 0;
for (int i = 0; i < taskCount; ++i)
{
sum += taskArray[i];
}
scheduleArray[0] = new bool[sum + 1];
for (int j = 0; j < sum + 1; ++j)
{
scheduleArray[0][j] = j == 0 || j == taskArray[0];
}
for (int i = 1; i < taskCount; ++i)
{
scheduleArray[i] = new bool[sum + 1];
for (int j = 0; j < sum + 1; ++j)
{
scheduleArray[i][j] = scheduleArray[i - 1][j] ||
j >= taskArray[i] &&
scheduleArray[i - 1][j - taskArray[i]];
}
}
getch(); // I'm reading memory usage from Task Manager when program stops here
int halfSum = sum >> 1;
while (!scheduleArray[taskCount - 1][halfSum]) --halfSum;
for (int i = taskCount - 1; i > 0; --i)
{
if (scheduleArray[i - 1][halfSum])
outP1Stack->push(i);
else if (scheduleArray[i - 1][halfSum - taskArray[i]])
{
outP2Stack->push(i);
halfSum -= taskArray[i];
}
}
if (halfSum) outP2Stack->push(0);
else outP1Stack->push(0);
}
else if (memorySaving == 1)
{
sum = 0;
for (int i = 0; i < taskCount; ++i)
{
sum += taskArray[i];
scheduleArray[0] = new bool[sum + 1];
for (int j = 0; j < sum + 1; ++j)
{
scheduleArray[0][j] = j == 0 || j == taskArray[0];
}
for (int i = 1; i < taskCount; ++i)
{
scheduleArray[i] = new bool[sum + 1];
for (int j = 0; j < sum + 1; ++j)
{
scheduleArray[i][j] = scheduleArray[i - 1][j] ||
j >= taskArray[i] &&
scheduleArray[i - 1][j - taskArray[i]];
}
}
}
getch(); // I'm reading memory usage from Task Manager when program stops here
int halfSum = sum >> 1;
while (!scheduleArray[taskCount - 1][halfSum]) --halfSum;
for (int i = taskCount - 1; i > 0; --i)
{
if (scheduleArray[i - 1][halfSum])
outP1Stack->push(i);
else if (scheduleArray[i - 1][halfSum - taskArray[i]])
{
outP2Stack->push(i);
halfSum -= taskArray[i];
}
}
if (halfSum) outP2Stack->push(0);
else outP1Stack->push(0);
}
for (int i = 0; i < taskCount; ++i)
{
delete[] scheduleArray[i];
}
delete[] scheduleArray;
}
int main()
{
char* filename = "input2.txt";
int memorySaving = 0; //changing to 1 in code when testing memory usage
int* taskArray; // each number in array equals time taken by task
int taskCount;
readTasks(filename, taskArray, taskCount);
coutTasks(taskArray, taskCount);
stack<int>* p1Stack = new stack<int>();
stack<int>* p2Stack = new stack<int>();
Scheduling2(taskArray, taskCount, memorySaving, p1Stack, p2Stack);
cout << "\np1: ";
while (p1Stack->size())
{
cout << p1Stack->top() << ", ";
p1Stack->pop();
}
cout << "\np2: ";
while (p2Stack->size())
{
cout << p2Stack->top() << ", ";
p2Stack->pop();
}
delete p1Stack;
delete p2Stack;
delete[] taskArray;
return 0;
}
答案 0 :(得分:2)
memorySaving == 1
注意到我正在分配(上帝知道为什么)我的数组的每一行taskCount
次......这完全不是我的意思,当我是写这个。好。那是深夜。
很抱歉打扰你们。问题应该结束。
答案 1 :(得分:1)
由于我的建议可以解决你的问题,如果你把它们带走了(我怀疑),我会给他们一个答案!
但是为什么在这种情况下使用vector会帮助我?我不需要使用它的任何功能。
是的,你做到了!您需要其中一个最重要的“功能”......阵列内存块的自动管理。请注意,如果您的声明为vector< vector<bool> > scheduleArray
,则无法发生泄露。您的代码中不会有任何新内容或删除内容......怎么可能?
使用vector的其他好处:
您不能在指针上意外执行delete
而不是delete[]
它可以进行边界检查(如果你启用它,你应该在你的调试版本中......只用vector<int> v; v[0] = 1;
尝试测试,以确保你打开它。)
Vector知道它所持有的元素数量,因此您不必遇到必须传递taskCount
等参数的情况。这消除了另一个您有机会在记账中出错的地方。 (例如,如果从向量中删除元素并忘记在计数变量中反映出来该怎么办?)
您的评论的答案:
移位操作的速度是否比除以2更快?
没有
如果您在原始程序集中进行编码,那么有时可能会在某些体系结构上进行编码。但是大多数情况下,整数除法和位移都会花费整个周期。而且我确信那里存在一些古怪的架构,它可以分裂得比它可以移动得快。
请记住,这是C ++,而不是汇编。最好保持代码清晰,并信任优化器做正确的事情。例如,如果SHIFT和DIV都需要一个指令周期,但是如果你因为管道的某些事情而处于严格的循环中,你可以通过交替使用更快的速度?
memorySaving将拥有比两个更多的值。
然后使用枚举类型。
std :: vector O(1)是否按索引访问每个元素,因为数组有?
是的,正如您所发现的那样。有small amount of per-vector overhead in terms of storage(因编译器而异)。但正如我上面提到的,这是一个很小的代价。此外,您通常可能首先在阵列外部的某个变量中跟踪长度。
至于指向堆栈的指针 - 通常情况下动态内存分配通常更好,因为我可以自己决定何时释放内存?
通常不更好,正是因为这个原因。如果你负责决定何时自己释放记忆,那么你可以放弃管理这个责任。
因此,只要有可能,您应该让C ++的范围处理对象的生命周期。有时制作一个在其创建范围之外存活的动态对象是无法避免的,但这就是Modern C ++有智能指针的原因。使用'em!
http://en.wikipedia.org/wiki/Smart_pointer
例如,如果readTasks
返回shared_ptr< vector<int> >
,则filename[0] = 'I';
会更干净,更安全。
为什么我要使用std :: string,如果我不使用它支持的任何函数,并且char *和std :: string一样好吗?
养成不使用它的习惯,原因是与vector的上述参数并行。例如,边界检查。另外,测验问题:如果您想要大写“input2.txt”并说{{1}},您认为会发生什么?
当您完成我的所有建议后,您可以查看boost::dynamic_bitset。 : - )