算法 - 同样根据两个标准填充不同大小的容器

时间:2013-03-19 17:19:49

标签: algorithm sorting

我试图围绕一个算法。我以前从未编写过算法编码,也不确定如何处理这个问题。这是它的主旨:

我可以拥有n个容器,每个容器都有两组对我很重要的数字:每个容器的内存量(x)和逻辑处理器数量(y)可以有不同的值。

每个虚拟机都有一个内存量(x)和多个逻辑处理器(y)。我正在尝试创建一种算法,该算法将平衡群集中所有主机上的内存(x)和多个逻辑处理器(y)的负载。它不会在所有主机中真正平等,但所有主机都在每个主机的10%+ / - 之内。

我怎么会在数学上考虑这个问题。

The illustration

2 个答案:

答案 0 :(得分:2)

如果我正确理解了您的问题,您希望最小化主机的相对负载,以便每个主机的负载与其他主机的偏差不超过10%。因此,我们希望通过找到最小值来优化主机之间的“相对负载”

为此,您可以使用某种Combinatorial Optimization来达到可接受的或最佳的解决方案。像Simulated AnnealingTabu Search这样的经典元启发式可以胜任。

问题的示例通用步骤:

  • 通过将每个VM随机分配给主机来定义初始状态
  • 通过在主机之间迭代交换VM来查找新状态,直到:
    • 找到了一些可接受的解决方案,或
    • 迭代次数已用尽,或
    • 满足一些其他条件(如模拟退火的“温度”)
  • 开发一个比较函数来决定何时在每次迭代中切换状态(解决方案)
    • 在您的情况下,您应该测量所有主机之间的相对负载,并且仅当新状态的相对负载低于当前状态时才测量交换状态。

这当然假设您将使用某种形式的逻辑表示而不是实际的VM来执行此算法。一旦找到模拟真实条件的解决方案,就可以将它们物理地应用到VM的/主机配置中。

希望这有帮助!

答案 1 :(得分:1)

你现在可能已经开始了,但是如果你回到这个问题,这个答案可能会有用。如果任何部分令人困惑,请告诉我,我会尝试澄清。

您的问题是2D可变尺寸纸盒包装没有旋转的情况。您的尺寸是内存和CPU,而不是长度和宽度(因此没有旋转)。

我会使用简单的离线打包算法。 (离线意味着您的VM和主机都是事先已知的) 我使用的简单包装是:

  1. 按所需内存对未分配的VM进行排序
  2. 按可用内存对主机进行排序
  3. 找到具有最适合VM的内存并将其分配给该主机的主机(确保也检查CPU容量。具有最多RAM的主机可能没有足够的CPU资源)
  4. 从列表中删除VM
  5. 降低主机的可用内存和CPU容量
  6. 如果您还有虚拟机,请转到2
  7. 以下是我定义虚拟机和主机的方式:

    [DebuggerDisplay("{Name}: {MemoryUsage} | {ProcessorUsage}")]
    class VirtualMachine
    {
        public int MemoryUsage;
        public string Name;
        public int ProcessorUsage;
    
        public VirtualMachine(string name, int memoryUsage, int processorUsage)
        {
            MemoryUsage = memoryUsage;
            ProcessorUsage = processorUsage;
            Name = name;
        }
    }
    
    [DebuggerDisplay("{Name}: {Memory} | {Processor}")]
    class Host
    {
        public readonly string Name;
        public int Memory;
        public Host Parent;
        public int Processor;
    
        public Host(string name, int memory, int processor, Host parent = null)
        {
            Name = name;
            Memory = memory;
            Processor = processor;
            Parent = parent;
        }
    
        public bool Fits(VirtualMachine vm) { return vm.MemoryUsage <= Memory && vm.ProcessorUsage <= Processor; }
        public Host Assign(VirtualMachine vm) { return new Host(Name + "_", Memory - vm.MemoryUsage, Processor - vm.ProcessorUsage, this); }
    }
    

    主机FitsAssign方法对于检查VM是否适合以及减少主机可用内存/ CPU非常重要。我创建了一个&#34; Host-Prime&#34;表示资源减少的主机,删除原始主机并将Host-Prime插入主机列表。 这是bin pack求解算法。如果您针对大型数据集运行,应该有很多机会加快执行速度,但这对于小型数据集来说已经足够了。

    class Allocator
    {
        readonly List<Host> Bins;
        readonly List<VirtualMachine> Items;
    
        public Allocator(List<Host> bins, List<VirtualMachine> items)
        {
            Bins = bins;
            Items = items;
        }
    
        public Dictionary<Host, List<VirtualMachine>> Solve()
        {
            var bins = new HashSet<Host>(Bins);
            var items = Items.OrderByDescending(item => item.MemoryUsage).ToList();
    
            var result = new Dictionary<Host, List<VirtualMachine>>();
    
            while (items.Count > 0)
            {
                var item = items.First();
                items.RemoveAt(0);
                var suitableBin = bins.OrderByDescending(b => b.Memory).FirstOrDefault(b => b.Fits(item));
                if (suitableBin == null)
                    return null;
    
                bins.Remove(suitableBin);
    
                var remainder = suitableBin.Assign(item);
                bins.Add(remainder);
    
                var rootBin = suitableBin;
                while (rootBin.Parent != null)
                    rootBin = rootBin.Parent;
                if (!result.ContainsKey(rootBin))
                    result[rootBin] = new List<VirtualMachine>();
                result[rootBin].Add(item);
            }
            return result;
        }
    }
    

    所以你现在有了一个打包算法,但是你仍然没有负载平衡解决方案。由于该算法将VM打包到主机上而不用考虑平衡内存使用,我们需要另一个级别的解决方案。为了实现一些粗略的记忆平衡,我采取了蛮力的方法。减少每个主机上的初始内存以表示目标使用目标。然后解决以查看您的VM是否适合可用的减少的内存。如果找不到解决方案,请放松内存约束。重复此操作直到找到解决方案,否则无法使用(使用给定的算法)。这应该给出最佳内存负载的粗略近似值。

    class Program
    {
        static void Main(string[] args)
        {
            //available hosts, probably loaded from a file or database
            var hosts = new List<Host> {new Host("A", 4096, 4), new Host("B", 8192, 8), new Host("C", 3072, 8), new Host("D", 3072, 8)};
            var hostLookup = hosts.ToDictionary(h => h.Name);
    
            //VMs required to run, probably loaded from a file or database
            var vms = new List<VirtualMachine>
                          {
                              new VirtualMachine("1", 512, 1),
                              new VirtualMachine("2", 1024, 2),
                              new VirtualMachine("3", 1536, 5),
                              new VirtualMachine("4", 1024, 8),
                              new VirtualMachine("5", 1024, 1),
                              new VirtualMachine("6", 2048, 1),
                              new VirtualMachine("7", 2048, 2)
                          };
    
            var solution = FindMinumumApproximateSolution(hosts, vms);
            if (solution == null)
                Console.WriteLine("No solution found.");
            else
                foreach (var hostAssigment in solution)
                {
                    var host = hostLookup[hostAssigment.Key.Name];
                    var vmsOnHost = hostAssigment.Value;
    
                    var xUsage = vmsOnHost.Sum(itm => itm.MemoryUsage);
                    var yUsage = vmsOnHost.Sum(itm => itm.ProcessorUsage);
                    var pctUsage = (xUsage / (double)host.Memory);
                    Console.WriteLine("{0} used {1} of {2} MB {5:P2} | {3} of {4} CPU", host.Name, xUsage, host.Memory, yUsage, host.Processor, pctUsage);
                    Console.WriteLine("\t VMs: " + String.Join(" ", vmsOnHost.Select(vm => vm.Name)));
                }
            Console.ReadKey();
        }
    
        static Dictionary<Host, List<VirtualMachine>> FindMinumumApproximateSolution(List<Host> hosts, List<VirtualMachine> vms)
        {
            for (var targetLoad = 0; targetLoad <= 100; targetLoad += 1)
            {
                var solution = GetTargetLoadSolution(hosts, vms, targetLoad / 100.0);
                if (solution == null)
                    continue;
                return solution;
            }
            return null;
        }
    
        static Dictionary<Host, List<VirtualMachine>> GetTargetLoadSolution(List<Host> hosts, List<VirtualMachine> vms, double targetMemoryLoad)
        {
            //create an alternate host list that reduces memory availability to the desired target
            var hostsAtTargetLoad = hosts.Select(h => new Host(h.Name, (int) (h.Memory * targetMemoryLoad), h.Processor)).ToList();
    
            var allocator = new Allocator(hostsAtTargetLoad, vms);
            return allocator.Solve();
        }
    }