C++ 选择机器组的数据结构
我有这个旧的批处理系统。调度器将所有计算节点存储在一个大数组中。大部分情况下这是可以的,因为大多数查询都可以通过过滤满足查询的节点来解决 我现在的问题是,除了一些基本属性(CPU数量、内存、操作系统),还有这些奇怪的分组属性(city、infiniband、network scratch) 现在的问题是,当用户使用infiniband请求节点时,我不能只给他任何节点,但我必须给他连接到一个infiniband交换机的节点,这样节点就可以使用infiniband进行通信 当用户只请求一个这样的属性时,这仍然可以(我可以为每个属性对数组进行分区,然后尝试分别选择每个分区中的节点) 问题在于组合多个这样的属性,因为这样我就必须生成所有子集的组合(主数组的分区) 好的方面是,大多数属性都位于子集或等价关系中(对于一个infiniband交换机上的机器位于一个城市来说,这有点道理)。但不幸的是,事实并非如此 是否有一些好的数据结构来存储这种半层次结构(主要是树状结构) 编辑:示例 用户请求:C++ 选择机器组的数据结构,c++,algorithm,data-structures,software-design,C++,Algorithm,Data Structures,Software Design,我有这个旧的批处理系统。调度器将所有计算节点存储在一个大数组中。大部分情况下这是可以的,因为大多数查询都可以通过过滤满足查询的节点来解决 我现在的问题是,除了一些基本属性(CPU数量、内存、操作系统),还有这些奇怪的分组属性(city、infiniband、network scratch) 现在的问题是,当用户使用infiniband请求节点时,我不能只给他任何节点,但我必须给他连接到一个infiniband交换机的节点,这样节点就可以使用infiniband进行通信 当用户只请求一个这样的属性
2x node with infiniband and networkfs
所需的输出将是:(node1,node2)
或(node5,node6)
或(node7,node8)
在一个好的情况下,这个例子不会发生,但在某些情况下,我们实际上有这些奇怪的跨站点连接。如果city2
中的节点都在infinibandswitch04
上,这将很容易。不幸的是,现在我必须生成具有相同infiniband交换机和相同网络文件系统的节点组
实际上,问题要复杂得多,因为用户不需要请求整个节点,而且属性也很多
编辑:为查询添加所需的输出。我的猜测是,不会有“简单、高效”的算法和数据结构来解决这个问题,因为您所做的类似于求解一组联立方程。假设总共有10个类别(如
城市
、infiniband
和网络
),用户为其中3个指定所需的值。比如说,用户要求5个节点。然后,您的任务是推断其余7个类别的值,这样至少存在5条记录,其中所有10个类别字段都等于这些值(指定的3个和推断的7个)。可能有多种解决方案
但是,如果没有太多不同的类别,并且在每个类别中没有太多不同的可能性,那么你可以做一个简单的暴力递归搜索来找到可能的解决方案,在递归的每一个级别,你考虑一个特定的类别,并且“尝试”每个可能性。假设用户要求提供
k
记录,并可通过required\u city
、required\u infiniband
等选择规定任何数量的要求:
either(x, y) := if defined(x) then [x] else y
For each city c in either(required_city, [city1, city2]):
For each infiniband i in either(required_infiniband, [switch03, switch04]):
For each networkfs nfs in either(required_nfs, [undefined, server01, server02]):
Do at least k records of type [c, i, nfs] exist? If so, return them.
other()
函数只是将搜索限制到包含用户为其指定约束的点的子空间的一种方法
基于此,您需要一种方法来快速查找任何给定
[c,i,nfs]
组合的点数(行)嵌套哈希表就可以了。假设您有p个分组属性和n台机器,基于bucket的解决方案最容易设置,并提供O(2p·log(n))访问和更新
- 您为每一组属性创建一个bucket堆(因此您将有一个用于“infiniband”的bucket堆、一个用于“networkfs”的bucket堆和一个用于“infiniband×networkfs”的bucket堆),这意味着2p个bucket堆李>
- 每个bucket堆包含一个用于每个值组合的bucket(因此“infiniband”bucket将包含一个用于键“switch04”的bucket和一个用于键“switch03”)这意味着在所有bucket堆中最多拆分n·2p个bucket李>
- 每个bucket是一个服务器列表(可能划分为可用和不可用)。bucket堆是一个标准堆(请参见
),其中每个bucket的值是该bucket中可用服务器的数量李>std::make_heap
- 每个服务器都存储对包含它的所有存储桶的引用李>
- 当您查找与某一组属性匹配的服务器时,您只需在相应的bucket中查找该属性组,然后沿着堆向下搜索一个足够大的bucket,以容纳请求的服务器数量。这需要O(log(p)·log(n))
- 当服务器被标记为可用或不可用时,您必须更新包含这些服务器的所有bucket,然后更新包含这些bucket的bucket堆。这是一个O(2p·log(n))操作李>
如果您发现自己的属性太多(2p变得失控),该算法允许根据需要从其他存储桶堆构建一些存储桶堆:如果用户请求“infiniband×networkfs”,但您只有一个可用于“infiniband”或“networkfs”的存储桶堆,则可以在“infiniband”中打开每个存储桶bucket heap单独放入bucket heap(使用惰性算法,以便在第一个bucket有效的情况下不必处理所有bucket),并使用惰性堆合并算法找到合适的bucket。然后,您可以使用LRU缓存来决定存储哪些属性组以及按需构建哪些属性组。步骤1:为每个属性创建索引。例如,对于每个属性+值对,创建具有该属性的节点的排序列表。将每个这样的列表放入某种关联数组中-类似于和stl映射,每个属性一个,按值索引。这样,当你完成时,你有一个接近常数的时间函数
either(x, y) := if defined(x) then [x] else y
For each city c in either(required_city, [city1, city2]):
For each infiniband i in either(required_infiniband, [switch03, switch04]):
For each networkfs nfs in either(required_nfs, [undefined, server01, server02]):
Do at least k records of type [c, i, nfs] exist? If so, return them.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace bleh
{
class Program
{
static void Main(string[] args)
{
List<Node> list = new List<Node>();
// create a random input list
Random r = new Random();
for (int i = 1; i <= 10000; i++)
{
Node node = new Node();
for (char c = 'a'; c <= 'z'; c++) node.Properties[c.ToString()] = (r.Next() % 10 + 1).ToString();
list.Add(node);
}
// if you have any absolute criteria, filter the list first according to it, which is very easy
// i am sure you know how to do that
// only look at relative criteria after removing nodes which are eliminated by absolute criteria
// example
List<string> criteria = new List<string> {"c", "h", "r", "x" };
criteria = criteria.OrderBy(x => x).ToList();
// order the list by each relative criteria, using a ***STABLE*** sort
foreach (string s in criteria)
list = list.OrderBy(x => x.Properties[s]).ToList();
// size of sought group
int n = 4;
// this is the algorithm
int sectionstart = 0;
int sectionend = 0;
for (int i = 1; i < list.Count; i++)
{
bool same = true;
foreach (string s in criteria) if (list[i].Properties[s] != list[sectionstart].Properties[s]) same = false;
if (same == true) sectionend = i;
else sectionstart = i;
if (sectionend - sectionstart == n - 1) break;
}
// print the results
Console.WriteLine("\r\nResult:");
for (int i = sectionstart; i <= sectionend; i++)
{
Console.Write("[" + i.ToString() + "]" + "\t");
foreach (string s in criteria) Console.Write(list[i].Properties[s] + "\t");
Console.WriteLine();
}
Console.ReadLine();
}
}
}
struct structNode
{
std::set<std::string> sMachines;
std::map<std::string, int> mCodeToIndex;
std::vector<structNode> vChilds;
};
void Fill(std::string strIdMachine, int iIndex, structNode* pNode, std::vector<std::string> &vCodes)
{
if(iIndex < vCodes.size())
{
// Add "Empty" if Needed
if(pNode->vChilds.size() == 0)
{
pNode->mCodeToIndex.insert(pNode->mCodeToIndex.begin(), make_pair("empty", 0));
pNode->vChilds.push_back(structNode());
}
// Add for "Empty"
pNode->vChilds[0].sMachines.insert(strIdMachine);
Fill(strIdMachine, (iIndex + 1), &pNode->vChilds[0], vCodes );
if(vCodes[iIndex] == "empty")
return;
// Add for "Any"
std::map<std::string, int>::iterator mIte = pNode->mCodeToIndex.find("any");
if(mIte == pNode->mCodeToIndex.end())
{
mIte = pNode->mCodeToIndex.insert(pNode->mCodeToIndex.begin(), make_pair("any", pNode->vChilds.size()));
pNode->vChilds.push_back(structNode());
}
pNode->vChilds[mIte->second].sMachines.insert(strIdMachine);
Fill(strIdMachine, (iIndex + 1), &pNode->vChilds[mIte->second], vCodes );
// Add for "Segment"
mIte = pNode->mCodeToIndex.find(vCodes[iIndex]);
if(mIte == pNode->mCodeToIndex.end())
{
mIte = pNode->mCodeToIndex.insert(pNode->mCodeToIndex.begin(), make_pair(vCodes[iIndex], pNode->vChilds.size()));
pNode->vChilds.push_back(structNode());
}
pNode->vChilds[mIte->second].sMachines.insert(strIdMachine);
Fill(strIdMachine, (iIndex + 1), &pNode->vChilds[mIte->second], vCodes );
}
}
//////////////////////////////////////////////////////////////////////
// Get
//
// NULL on empty group
//////////////////////////////////////////////////////////////////////
set<std::string>* Get(structNode* pNode, int iIndex, vector<std::string> vCodes, int iMinValue)
{
if(iIndex < vCodes.size())
{
std::map<std::string, int>::iterator mIte = pNode->mCodeToIndex.find(vCodes[iIndex]);
if(mIte != pNode->mCodeToIndex.end())
{
if(pNode->vChilds[mIte->second].sMachines.size() < iMinValue)
return NULL;
else
return Get(&pNode->vChilds[mIte->second], (iIndex + 1), vCodes, iMinValue);
}
else
return NULL;
}
return &pNode->sMachines;
}
structNode stRoot;
const char* dummy[] = { "city1", "switch03", "server01" };
const char* dummy2[] = { "city1", "switch03", "empty" };
const char* dummy3[] = { "city2", "switch03", "server02" };
const char* dummy4[] = { "city2", "switch04", "server02" };
// Fill the tree with the sample
Fill("node1", 0, &stRoot, vector<std::string>(dummy, dummy + 3));
Fill("node2", 0, &stRoot, vector<std::string>(dummy, dummy + 3));
Fill("node3", 0, &stRoot, vector<std::string>(dummy2, dummy2 + 3));
Fill("node4", 0, &stRoot, vector<std::string>(dummy2, dummy2 + 3));
Fill("node5", 0, &stRoot, vector<std::string>(dummy3, dummy3 + 3));
Fill("node6", 0, &stRoot, vector<std::string>(dummy3, dummy3 + 3));
Fill("node7", 0, &stRoot, vector<std::string>(dummy4, dummy4 + 3));
Fill("node8", 0, &stRoot, vector<std::string>(dummy4, dummy4 + 3));
vector<std::string> vCodes;
vCodes.push_back("empty"); // Discard first property (cities)
vCodes.push_back("any"); // Any value for infiniband
vCodes.push_back("any"); // Any value for networkfs (except empty)
set<std::string>* pMachines = Get(&stRoot, 0, vCodes, 2);
vector<std::string> vCodes;
vCodes.push_back("city2"); // Only city2
vCodes.push_back("switch03"); // Only switch03
vCodes.push_back("any"); // Any value for networkfs (except empy)
set<std::string>* pMachines = Get(&stRoot, 0, vCodes, 2);