org.apache.flink.runtime.clusterframework.types.ResourceProfile Maven / Gradle / Ivy
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.flink.runtime.clusterframework.types;
import org.apache.flink.api.common.operators.ResourceConstraints;
import org.apache.flink.api.common.operators.ResourceSpec;
import org.apache.flink.api.common.resources.Resource;
import javax.annotation.Nonnull;
import java.io.Serializable;
import java.util.Collections;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.Objects;
/**
* Describe the immutable resource profile of the slot, either when requiring or offering it. The profile can be
* checked whether it can match another profile's requirement, and furthermore we may calculate a matching
* score to decide which profile we should choose when we have lots of candidate slots.
* It should be generated from {@link ResourceSpec} with the input and output memory calculated in JobMaster.
*
* Resource Profiles have a total ordering, defined by comparing these fields in sequence:
*
* - Memory Size
* - CPU cores
* - Extended resources
*
* The extended resources are compared ordered by the resource names.
*/
public class ResourceProfile implements Serializable, Comparable {
private static final long serialVersionUID = 1L;
public static final ResourceProfile UNKNOWN = new ResourceProfile(-1.0, -1);
public static final ResourceProfile EMTPY = new ResourceProfile(0, 0);
// ------------------------------------------------------------------------
/** How many cpu cores are needed, use double so we can specify cpu like 0.1. */
private double cpuCores;
/** How many heap memory in mb are needed. */
private int heapMemoryInMB;
/** How many direct memory in mb are needed. */
private int directMemoryInMB;
/** How many native memory in mb are needed. */
private int nativeMemoryInMB;
/** Memory used for the task in the slot to communicate with its upstreams. Set by job master. */
private int networkMemoryInMB;
/** A extensible field for user specified resources from {@link ResourceSpec}. */
private Map extendedResources = new HashMap<>(1);
/** The hash code of this {@link ResourceProfile}. */
private volatile int hashResult;
/** Resource constraints.*/
private final ResourceConstraints resourceConstraints;
// ------------------------------------------------------------------------
/**
* Creates a new ResourceProfile.
*
* @param cpuCores The number of CPU cores (possibly fractional, i.e., 0.2 cores)
* @param heapMemoryInMB The size of the heap memory, in megabytes.
* @param directMemoryInMB The size of the direct memory, in megabytes.
* @param nativeMemoryInMB The size of the native memory, in megabytes.
* @param networkMemoryInMB The size of the memory for input and output, in megabytes.
* @param extendedResources The extended resources such as GPU and FPGA
*/
public ResourceProfile(
double cpuCores,
int heapMemoryInMB,
int directMemoryInMB,
int nativeMemoryInMB,
int networkMemoryInMB,
Map extendedResources,
ResourceConstraints resourceConstraints) {
this.cpuCores = cpuCores;
this.heapMemoryInMB = heapMemoryInMB;
this.directMemoryInMB = directMemoryInMB;
this.nativeMemoryInMB = nativeMemoryInMB;
this.networkMemoryInMB = networkMemoryInMB;
if (extendedResources != null) {
this.extendedResources.putAll(extendedResources);
}
this.resourceConstraints = resourceConstraints;
updateHashCode();
}
public ResourceProfile(
double cpuCores,
int heapMemoryInMB,
int directMemoryInMB,
int nativeMemoryInMB,
int networkMemoryInMB,
Map extendedResources) {
this(cpuCores, heapMemoryInMB, directMemoryInMB, nativeMemoryInMB, networkMemoryInMB, extendedResources, null);
}
/**
* Creates a new simple ResourceProfile used for testing.
*
* @param cpuCores The number of CPU cores (possibly fractional, i.e., 0.2 cores)
* @param heapMemoryInMB The size of the heap memory, in megabytes.
*/
public ResourceProfile(double cpuCores, int heapMemoryInMB) {
this(cpuCores, heapMemoryInMB, 0, 0, 0, Collections.emptyMap());
}
/**
* Creates a copy of the given ResourceProfile.
*
* @param other The ResourceProfile to copy.
*/
public ResourceProfile(ResourceProfile other) {
this(other.cpuCores,
other.heapMemoryInMB,
other.directMemoryInMB,
other.nativeMemoryInMB,
other.networkMemoryInMB,
other.extendedResources,
other.resourceConstraints);
}
// ------------------------------------------------------------------------
public ResourceConstraints getResourceConstraints() {
return resourceConstraints;
}
/**
* Get the cpu cores needed.
*
* @return The cpu cores, 1.0 means a full cpu thread
*/
public double getCpuCores() {
return cpuCores;
}
/**
* Get the heap memory needed in MB.
*
* @return The heap memory in MB
*/
public int getHeapMemoryInMB() {
return heapMemoryInMB;
}
/**
* Get the direct memory needed in MB.
*
* @return The direct memory in MB
*/
public int getDirectMemoryInMB() {
return directMemoryInMB;
}
/**
* Get the native memory needed in MB.
*
* @return The native memory in MB
*/
public int getNativeMemoryInMB() {
return nativeMemoryInMB;
}
/**
* Get the memory needed for task to communicate with its upstreams and downstreams in MB.
* @return The network memory in MB
*/
public int getNetworkMemoryInMB() {
return networkMemoryInMB;
}
/**
* Get the total memory needed in MB.
*
* @return The total memory in MB
*/
public int getMemoryInMB() {
return heapMemoryInMB + directMemoryInMB + nativeMemoryInMB + networkMemoryInMB;
}
/**
* Get the memory the operators needed in MB.
*
* @return The operator memory in MB
*/
public int getOperatorsMemoryInMB() {
return heapMemoryInMB + directMemoryInMB + nativeMemoryInMB;
}
/**
* Get the extended resources.
*
* @return The extended resources
*/
public Map getExtendedResources() {
return Collections.unmodifiableMap(extendedResources);
}
/**
* Get the managed memory of task manager.
* @return The managed memory in MB
*/
public int getManagedMemoryInMB() {
Resource managedMemory = extendedResources.get(ResourceSpec.MANAGED_MEMORY_NAME);
if (managedMemory != null) {
return (int)managedMemory.getValue();
}
return 0;
}
/**
* Get the floating memory needed in MB
* @return The floating memory in MB
*/
public int getFloatingManagedMemoryInMB() {
Resource floatingMemory = extendedResources.get(ResourceSpec.FLOATING_MANAGED_MEMORY_NAME);
if (floatingMemory != null) {
return (int) floatingMemory.getValue();
}
return 0;
}
/**
* Check whether required resource profile can be matched.
*
* @param required the required resource profile
* @return true if the requirement is matched, otherwise false
*/
public boolean isMatching(ResourceProfile required) {
// Currently, a slot can be used only if the required ResourceConstraints strictly
// match the slot's ResourceConstraints.
if (resourceConstraints != null) {
if(!resourceConstraints.equals(required.getResourceConstraints())) {
return false;
}
} else if (required.getResourceConstraints() != null) {
return false;
}
if (cpuCores >= required.getCpuCores() &&
heapMemoryInMB >= required.getHeapMemoryInMB() &&
directMemoryInMB >= required.getDirectMemoryInMB() &&
nativeMemoryInMB >= required.getNativeMemoryInMB() &&
networkMemoryInMB >= required.getNetworkMemoryInMB()) {
for (Map.Entry resource : required.extendedResources.entrySet()) {
// Skip floating memory, floating memory will not be considered when findMatchingSlot in slot manager
if (resource.getKey().equals(ResourceSpec.FLOATING_MANAGED_MEMORY_NAME)) {
continue;
}
if (!extendedResources.containsKey(resource.getKey()) ||
!extendedResources.get(resource.getKey()).getResourceAggregateType().equals(resource.getValue().getResourceAggregateType()) ||
extendedResources.get(resource.getKey()).getValue() < resource.getValue().getValue()) {
return false;
}
}
return true;
}
return false;
}
@Override
public int compareTo(@Nonnull ResourceProfile other) {
int cmp = Integer.compare(this.getMemoryInMB(), other.getMemoryInMB());
if (cmp != 0) {
return cmp;
}
cmp = Double.compare(this.cpuCores, other.cpuCores);
if (cmp != 0) {
return cmp;
}
if (!extendedResources.isEmpty() && !other.extendedResources.isEmpty()) {
boolean hasUniqueResource = (extendedResources.size() != other.extendedResources.size());
for (Map.Entry resourceEntry : extendedResources.entrySet()) {
Resource otherResource = other.extendedResources.get(resourceEntry.getKey());
if (otherResource == null) {
hasUniqueResource = true;
continue;
}
Resource thisResource = resourceEntry.getValue();
if (!otherResource.getResourceAggregateType().equals(thisResource.getResourceAggregateType())) {
return 1;
}
if ((cmp = Double.compare(thisResource.getValue(), otherResource.getValue())) != 0) {
return cmp;
}
}
if (hasUniqueResource) {
// All the common resources are equal but at least one of them has unique resource(s).
// In this case just use hash code to get a consistent comparison result.
return this.hashCode() > other.hashCode() ? 1 : -1;
}
} else {
// At least one of them has no extended resource.
cmp = extendedResources.size() - other.extendedResources.size();
}
if (cmp == 0) {
if (resourceConstraints == null && other.getResourceConstraints() != null) {
return 1;
} else if (resourceConstraints != null && other.getResourceConstraints() == null) {
return -1;
} else if (resourceConstraints != null && other.resourceConstraints != null) {
Iterator> thisIterator =
resourceConstraints.getConstraints().entrySet().iterator();
Iterator> otherIterator =
other.resourceConstraints.getConstraints().entrySet().iterator();
// seems meaningless, are there other more appropriate ways to compare two resource constraints?
while (thisIterator.hasNext() && otherIterator.hasNext()) {
Map.Entry thisConstraint = thisIterator.next();
Map.Entry otherConstraint = otherIterator.next();
if ((cmp = thisConstraint.getKey().compareTo(otherConstraint.getKey())) != 0) {
return cmp;
}
if ((cmp = thisConstraint.getValue().compareTo(otherConstraint.getValue())) != 0) {
return cmp;
}
}
if (thisIterator.hasNext()) {
return -1;
}
if (otherIterator.hasNext()) {
return 1;
}
}
}
return cmp;
}
/**
* Compute the result of subtract another resource from this piece of resource. The extended resource not included in this
* resource will cause IllegalArgumentException.
*
* This method do not check whether enough resource is present. The caller of this method is responsible to do the checking.
*
* @param another The resource to subtract.
* @return The result of subtract another resource from this resource.
*
* @throws IllegalArgumentException The extended resource not included in this resource will cause IllegalArgumentException.
*/
public ResourceProfile minus(ResourceProfile another) {
for (String extendedResourceName : another.extendedResources.keySet()) {
if (!extendedResources.containsKey(extendedResourceName)) {
throw new IllegalArgumentException("Non-exist extended resource: " + extendedResourceName);
}
}
Map newExtendedResource = new HashMap(extendedResources.size());
for (Map.Entry entry : extendedResources.entrySet()) {
Resource anotherResource = another.extendedResources.get(entry.getKey());
if (anotherResource == null) {
newExtendedResource.put(entry.getKey(), entry.getValue());
} else {
newExtendedResource.put(entry.getKey(), entry.getValue().minus(anotherResource));
}
}
return new ResourceProfile(
cpuCores - another.cpuCores,
heapMemoryInMB - another.heapMemoryInMB,
directMemoryInMB - another.directMemoryInMB,
nativeMemoryInMB - another.nativeMemoryInMB,
networkMemoryInMB - another.networkMemoryInMB,
newExtendedResource, resourceConstraints);
}
public ResourceProfile merge(ResourceProfile another) {
ResourceProfile resourceProfile = new ResourceProfile(this);
resourceProfile.addTo(another);
return resourceProfile;
}
public void addTo(ResourceProfile another) {
this.cpuCores += another.getCpuCores();
this.heapMemoryInMB += another.getHeapMemoryInMB();
this.directMemoryInMB += another.getDirectMemoryInMB();
this.nativeMemoryInMB += another.getNativeMemoryInMB();
this.networkMemoryInMB += another.getNetworkMemoryInMB();
if (!extendedResources.isEmpty() || !another.extendedResources.isEmpty()) {
for (Map.Entry extendResource : another.extendedResources.entrySet()) {
Resource rfValue = extendedResources.get(extendResource.getKey());
if (rfValue != null) {
extendedResources.put(extendResource.getKey(), extendResource.getValue().merge(rfValue));
} else {
extendedResources.put(extendResource.getKey(), extendResource.getValue());
}
}
}
updateHashCode();
}
public ResourceProfile multiply(int multiplier) {
Map newExtendedResource = new HashMap<>(extendedResources.size());
for (Map.Entry entry : extendedResources.entrySet()) {
newExtendedResource.put(entry.getKey(), entry.getValue().multiply(multiplier));
}
return new ResourceProfile(
this.getCpuCores() * multiplier,
this.getHeapMemoryInMB() * multiplier,
this.getDirectMemoryInMB() * multiplier,
this.getNativeMemoryInMB() * multiplier,
this.getNetworkMemoryInMB() * multiplier,
newExtendedResource, this.resourceConstraints);
}
// ------------------------------------------------------------------------
@Override
public int hashCode() {
return hashResult;
}
private void updateHashCode() {
final long cpuBits = Double.doubleToLongBits(cpuCores);
int result = (int) (cpuBits ^ (cpuBits >>> 32));
result = 31 * result + heapMemoryInMB;
result = 31 * result + directMemoryInMB;
result = 31 * result + nativeMemoryInMB;
result = 31 * result + networkMemoryInMB;
result = 31 * result + extendedResources.hashCode();
if (resourceConstraints != null) {
result = 31 * result + resourceConstraints.hashCode();
}
this.hashResult = result;
}
@Override
public boolean equals(Object obj) {
if (obj == this) {
return true;
}
else if (obj != null && obj.getClass() == ResourceProfile.class) {
ResourceProfile that = (ResourceProfile) obj;
if (this.hashCode() != that.hashCode()) {
return false;
}
return this.cpuCores == that.cpuCores &&
this.heapMemoryInMB == that.heapMemoryInMB &&
this.directMemoryInMB == that.directMemoryInMB &&
this.nativeMemoryInMB == that.nativeMemoryInMB &&
this.networkMemoryInMB == that.networkMemoryInMB &&
Objects.equals(extendedResources, that.extendedResources) &&
((this.resourceConstraints != null
&& this.resourceConstraints.equals(that.resourceConstraints)) ||
(this.resourceConstraints == null && that.resourceConstraints == null));
}
return false;
}
@Override
public String toString() {
String strExtendedResource = "";
if (!extendedResources.isEmpty()) {
final StringBuilder resources = new StringBuilder(extendedResources.size() * 10);
for (Map.Entry resource : extendedResources.entrySet()) {
resources.append(", ").append(resource.getKey()).append('=').append(resource.getValue().getValue());
}
strExtendedResource = resources.toString();
}
String constraintsStr = null;
if (resourceConstraints != null) {
constraintsStr = resourceConstraints.toString();
}
return "ResourceProfile{" +
"cpuCores=" + cpuCores +
", heapMemoryInMB=" + heapMemoryInMB +
", directMemoryInMB=" + directMemoryInMB +
", nativeMemoryInMB=" + nativeMemoryInMB +
", networkMemoryInMB=" + networkMemoryInMB + strExtendedResource +
", ResourceConstraints=" + constraintsStr +
'}';
}
public static ResourceProfile fromResourceSpec(ResourceSpec resourceSpec, int networkMemory) {
Map copiedExtendedResources = new HashMap<>(resourceSpec.getExtendedResources());
return new ResourceProfile(
resourceSpec.getCpuCores(),
resourceSpec.getHeapMemory(),
resourceSpec.getDirectMemory(),
resourceSpec.getNativeMemory(),
networkMemory,
copiedExtendedResources);
}
public static ResourceProfile fromResourceSpec(ResourceSpec resourceSpec,
ResourceConstraints resourceConstraints,
int networkMemory) {
Map copiedExtendedResources = new HashMap<>(resourceSpec.getExtendedResources());
return new ResourceProfile(
resourceSpec.getCpuCores(),
resourceSpec.getHeapMemory(),
resourceSpec.getDirectMemory(),
resourceSpec.getNativeMemory(),
networkMemory,
copiedExtendedResources,
resourceConstraints);
}
}