用于选择机器组的数据结构

时间:2012-06-26 12:26:20

标签: c++ algorithm data-structures software-design

我有这个旧的批处理系统。调度程序将所有计算节点存储在一个大数组中。现在大多数情况下都可以,因为大多数查询可以通过筛选满足查询的节点来解决。

我现在遇到的问题是除了一些基本属性(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交换机和相同网络文件系统的节点组。

实际上问题要复杂得多,因为用户不会请求整个节点,而且属性很多。

编辑:为查询添加了所需的输出。

5 个答案:

答案 0 :(得分:3)

假设您有p个分组属性和n个机器,基于桶的解决方案最容易设置并提供O(2 p ·log(n))访问和更新。

  • 您为每组属性创建一个存储桶堆(因此您将拥有“infiniband”的存储桶堆,“networkfs”的存储桶堆和“infiniband×networkfs”的存储桶堆) - 这表示2 p 桶堆。
  • 每个桶堆包含一个用于每个值组合的桶(因此“infiniband”桶包含用于键“switch04”的桶和用于键“switch03”的桶) - 这意味着总共最多n·2 p 桶分散在所有桶堆中。
  • 每个存储桶都是服务器列表(可能已分区为可用和不可用)。存储桶堆是标准堆(请参阅std::make_heap),其中每个存储桶的值是该存储桶中可用服务器的数量。
  • 每个服务器都存储对包含它的所有存储桶的引用。
  • 当您查找与特定属性组匹配的服务器时,您只需查看该属性组的相应存储桶,然后向下爬堆,查找足够大的存储桶以容纳所请求的服务器数量。这需要O(log(p)·log(n))。
  • 当服务器标记为可用或不可用时,您必须更新包含这些服务器的所有存储桶,然后更新包含这些存储桶的存储桶堆。这是一个O(2 p ·log(n))操作。

如果您发现自己拥有太多属性(并且2 p 失控),该算法允许从其他存储堆中按需构建一些存储桶堆:如果用户请求“infiniband×networkfs”,但您只有“infiniband”或“networkfs”可用的存储桶堆,您可以将“infiniband”存储桶堆中的每个存储桶自行转换为存储桶堆(使用延迟算法,因此您不必处理所有存储桶(如果第一个工作)并使用惰性堆合并算法来查找适当的存储桶。然后,您可以使用LRU缓存来确定存储哪些属性组以及哪些属性组是按需构建的。

答案 1 :(得分:0)

我的猜测是,没有一个“简单,高效”的算法和数据结构来解决这个问题,因为你所做的就像解决一组联立方程式。假设总共有10个类别(例如cityinfinibandnetwork),并且用户为其中3个指定了所需的值。比方说,用户要求5个节点。然后,您的任务是推断剩余7个类别的值,这样至少存在5条记录,其中所有10个类别字段都等于这些值(指定的3和推断的7)。可能有多种解决方案。

尽管如此,如果没有太多不同的类别,并且每个类别中没有太多不同的可能性,您可以进行简单的强力递归搜索以找到可能的解决方案,其中在每个递归级别您考虑特定类别,并“尝试”每种可能性。假设用户要求k条记录,并可以选择通过required_cityrequired_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);