我有这个旧的批处理系统。调度程序将所有计算节点存储在一个大数组中。现在大多数情况下都可以,因为大多数查询可以通过筛选满足查询的节点来解决。
我现在遇到的问题是除了一些基本属性(cpus,内存,操作系统的数量)之外,还有这些奇怪的分组属性(城市,infiniband,网络划痕)。
现在问题是,当用户请求具有infiniband的节点时,我不能只给他任何节点,但我必须给他连接到一个infiniband交换机的节点,所以节点实际上可以使用infiniband进行通信。 / p>
当用户只请求一个这样的属性时,这仍然没问题(我可以为每个属性分配数组,然后尝试分别选择每个分区中的节点)。
问题在于组合多个这样的属性,因为那时我将不得不生成子集的所有组合(主阵列的分区)。
好处是大多数属性都处于子集或等价关系中(对于一个infiniband交换机上的计算机来说,它在一个城市中是有意义的)。但不幸的是,这并不完全正确。
是否存在一些良好的数据结构来存储这种半分层的大多数树状的东西?
编辑:示例
node1 : city=city1, infiniband=switch03, networkfs=server01
node2 : city=city1, infiniband=switch03, networkfs=server01
node3 : city=city1, infiniband=switch03
node4 : city=city1, infiniband=switch03
node5 : city=city2, infiniband=switch03, networkfs=server02
node6 : city=city2, infiniband=switch03, networkfs=server02
node7 : city=city2, infiniband=switch04, networkfs=server02
node8 : city=city2, infiniband=switch04, networkfs=server02
用户请求:
2x node with infiniband and networkfs
所需的输出为:(node1, node2)
或(node5,node6)
或(node7,node8)
。
在一个好的情况下,这个例子不会发生,但在某些情况下我们实际上有这些奇怪的跨站点连接。如果city2
中的节点全部在infiniband switch04
上,那将很容易。不幸的是,现在我必须生成具有相同infiniband交换机和相同网络文件系统的节点组。
实际上问题要复杂得多,因为用户不会请求整个节点,而且属性很多。
编辑:为查询添加了所需的输出。
答案 0 :(得分:3)
假设您有p个分组属性和n个机器,基于桶的解决方案最容易设置并提供O(2 p ·log(n))访问和更新。
std::make_heap
),其中每个存储桶的值是该存储桶中可用服务器的数量。 如果您发现自己拥有太多属性(并且2 p 失控),该算法允许从其他存储堆中按需构建一些存储桶堆:如果用户请求“infiniband×networkfs”,但您只有“infiniband”或“networkfs”可用的存储桶堆,您可以将“infiniband”存储桶堆中的每个存储桶自行转换为存储桶堆(使用延迟算法,因此您不必处理所有存储桶(如果第一个工作)并使用惰性堆合并算法来查找适当的存储桶。然后,您可以使用LRU缓存来确定存储哪些属性组以及哪些属性组是按需构建的。
答案 1 :(得分:0)
我的猜测是,没有一个“简单,高效”的算法和数据结构来解决这个问题,因为你所做的就像解决一组联立方程式。假设总共有10个类别(例如city
,infiniband
和network
),并且用户为其中3个指定了所需的值。比方说,用户要求5个节点。然后,您的任务是推断剩余7个类别的值,这样至少存在5条记录,其中所有10个类别字段都等于这些值(指定的3和推断的7)。可能有多种解决方案。
尽管如此,如果没有太多不同的类别,并且每个类别中没有太多不同的可能性,您可以进行简单的强力递归搜索以找到可能的解决方案,其中在每个递归级别您考虑特定类别,并“尝试”每种可能性。假设用户要求k
条记录,并可以选择通过required_city
,required_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.
either()
函数只是一种将搜索限制在包含用户为其提供约束的点的子空间的方法。
基于此,您需要一种快速查找任何给定[c, i, nfs]
组合的点(行)数量的方法 - 嵌套哈希表对此可以正常工作。
答案 2 :(得分:0)
步骤1:为每个属性创建索引。例如。对于每个属性+值对,创建具有该属性的节点的排序列表。将每个这样的列表放入某种关联数组中 - 这类似于和stl map,每个属性一个,由值索引。这样,当你完成后,你有一个接近恒定时间的函数,它可以返回一个与单个属性+值对匹配的节点列表。该列表只是按节点编号排序。
步骤2:给定查询,对于所需的每个属性+值对,检索节点列表。
步骤3:从最短列表开始,将其称为列表0,将其与其他每个列表进行比较,然后从列表0中删除不在其他列表中的元素。
您现在应该只拥有请求所有属性的节点。
您的另一个选择是使用数据库,它已经设置为支持这样的查询。它可以在内存中完成,例如带有SQL扩展的BerkeleyDB。
答案 3 :(得分:-1)
如果根据查询中提到的每个条件对列表进行排序是可行的(或者按照每个相对标准对列表进行预先排序),这非常有效。
通过"相对标准",我的意思是不是形式的标准" x必须是5",这对于过滤是微不足道的,但是形式的标准" x对于结果集中的每个项目必须相同"。如果还有&#34的标准; x必须是5"形式,然后首先过滤,然后执行以下操作。
它依赖于对多列使用稳定排序来快速查找匹配组(而不尝试组合)。
复杂性是节点数*查询中的标准数(对于算法本身)+节点数*对数(节点数)*标准数(对于排序,如果不是预排序)。所以节点*日志(节点)*标准。
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();
}
}
}
答案 4 :(得分:-1)
我会做这样的事情(显然代替字符串你应该将它们映射到int,并使用int作为代码)
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);
例如,在switch03上只有City02,networfs不为空
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);