Clean up release tests (#11420)

This commit is contained in:
Barak Michener
2020-10-23 16:14:25 -07:00
committed by Alex Wu
parent 1034083988
commit 2d9b7355ba
55 changed files with 341 additions and 441 deletions
+2
View File
@@ -0,0 +1,2 @@
export ray_version="1.0.0rc1"
export commit=fd5ddb661e659c2b0c968661d96d0405426912e5
+2
View File
@@ -0,0 +1,2 @@
Running the kickoff script:
+1
View File
@@ -0,0 +1 @@
../doc/dev/RELEASE_PROCESS.rst
+29
View File
@@ -0,0 +1,29 @@
#!/usr/bin/env bash
source "$2"
ray_version=${ray_version:-}
commit=${commit:-}
if [[ $ray_version == "" || $commit == "" || $1 == "" ]]
then
echo "Provide --ray-version, --commit, and --ray-branch"
exit 1
fi
echo "version: $ray_version"
echo "commit: $commit"
echo "workload: $1"
DATESTR=$(date +%Y%m%d-%H%M)
SESSION="$1-$DATESTR"
echo "session: $SESSION"
chmod +x ./run.sh
if [ -z "$NO_UP" ]; then
anyscale up "$SESSION"
fi
anyscale push "$SESSION"
anyscale exec -n "$SESSION" "./run.sh $1 --ray-version=$ray_version --commit=$commit"
@@ -0,0 +1,45 @@
Long Running Distributed Tests
==============================
This directory contains the long-running multi-node workloads which are intended to run
forever until they fail. To set up the project you need to run
.. code-block:: bash
$ pip install anyscale
$ anyscale init
Running the Workloads
---------------------
Easiest approach is to use the `Anyscale UI <https://www.anyscale.dev/>`_. First run ``anyscale snapshot create`` from the command line to create a project snapshot. Then from the UI, you can launch an individual session and execute the test_workload command for each test.
You can also start the workloads using the CLI with:
.. code-block:: bash
$ anyscale start --ray-wheel=<RAY_WHEEL_LINK>
$ anyscale run test_workload --workload=<WORKLOAD_NAME>
Doing this for each workload will start one EC2 instance per workload and will start the workloads
running (one per instance). A list of
available workload options is available in the `ray_projects/project.yaml` file.
Debugging
---------
The primary method to debug the test while it is running is to view the logs and the dashboard from the UI. After the test has failed, you can still view the stdout logs in the UI and also inspect
the logs under ``/tmp/ray/session*/logs/`` and
``/tmp/ray/session*/debug_state.txt``.
Shut Down the Workloads
-----------------------
The instances running the workloads can all be killed by running
``anyscale stop <SESSION_NAME>``.
Adding a Workload
-----------------
To create a new workload, simply add a new Python file under ``workloads/`` and
add the workload in the run command in `ray-project/project.yaml`.
@@ -0,0 +1,73 @@
# This file is generated by `ray project create`.
# A unique identifier for the head node and workers of this cluster.
cluster_name: long-running-distributed-tests
# The minimum number of workers nodes to launch in addition to the head
# node. This number should be >= 0.
min_workers: 3
# The maximum number of workers nodes to launch in addition to the head
# node. This takes precedence over min_workers. min_workers defaults to 0.
max_workers: 3
# The autoscaler will scale up the cluster to this target fraction of resource
# usage. For example, if a cluster of 10 nodes is 100% busy and
# target_utilization is 0.8, it would resize the cluster to 13. This fraction
# can be decreased to increase the aggressiveness of upscaling.
# This value must be less than 1.0 for scaling to happen.
target_utilization_fraction: 0.8
# If a node is idle for this many minutes, it will be removed.
idle_timeout_minutes: 5
# Cloud-provider specific configuration.
provider:
type: aws
region: us-west-2
availability_zone: us-west-2a
cache_stopped_nodes: False
# How Ray will authenticate with newly launched nodes.
auth:
ssh_user: ubuntu
# By default Ray creates a new private keypair, but you can also use your own.
# If you do so, make sure to also set "KeyName" in the head and worker node
# configurations below.
# ssh_private_key: /path/to/your/key.pem
# Provider-specific config for the head node, e.g. instance type. By default
# Ray will auto-configure unspecified fields such as SubnetId and KeyName.
# For more documentation on available fields, see:
# http://boto3.readthedocs.io/en/latest/reference/services/ec2.html#EC2.ServiceResource.create_instances
head_node:
InstanceType: g3.8xlarge
ImageId: ami-0888a3b5189309429 # DLAMI 7/1/19
BlockDeviceMappings:
- DeviceName: /dev/sda1
Ebs:
VolumeSize: 150
worker_nodes:
InstanceType: g3.8xlarge
ImageId: ami-0888a3b5189309429 # DLAMI 7/1/19
BlockDeviceMappings:
- DeviceName: /dev/sda1
Ebs:
VolumeSize: 150
InstanceMarketOptions:
MarketType: spot
setup_commands: []
# Command to start ray on the head node. You don't need to change this.
head_start_ray_commands:
- ray stop
- export RAY_BACKEND_LOG_LEVEL=debug
- ray start --head --port=6379 --object-manager-port=8076 --autoscaling-config=~/ray_bootstrap_config.yaml
# Command to start ray on worker nodes. You don't need to change this.
worker_start_ray_commands:
- ray stop
- export RAY_BACKEND_LOG_LEVEL=debug
- ray start --address=$RAY_HEAD_IP:6379 --object-manager-port=8076
+61
View File
@@ -0,0 +1,61 @@
#!/usr/bin/env bash
ray_version=""
commit=""
ray_branch=""
workload=""
usage() {
echo "Start one microbenchmark trial."
}
for i in "$@"
do
echo "$i"
case "$i" in
--ray-version=*)
ray_version="${i#*=}"
;;
--commit=*)
commit="${i#*=}"
;;
--ray-branch=*)
ray_branch="${i#*=}"
;;
--workload=*)
workload="${i#*=}"
;;
--help)
usage
exit
;;
*)
echo "unknown arg, $i"
exit 1
;;
esac
done
echo "version: $ray_version"
echo "commit: $commit"
echo "branch: $ray_branch"
echo "workload: $workload"
wheel="https://s3-us-west-2.amazonaws.com/ray-wheels/$ray_branch/$commit/ray-$ray_version-cp36-cp36m-manylinux1_x86_64.whl"
conda uninstall -y terminado || true
pip install -U pip
pip install terminado
pip install -U "$wheel"
pip install "ray[rllib]"
pip install -U ipdb
# There have been some recent problems with torch 1.5 and torchvision 0.6
# not recognizing GPUs.
# So, we force install torch 1.4 and torchvision 0.5.
# https://github.com/pytorch/pytorch/issues/37212#issuecomment-623198624.
pip install torch==1.4.0 torchvision==0.5.0
echo set-window-option -g mouse on > ~/.tmux.conf
echo 'termcapinfo xterm* ti@:te@' > ~/.screenrc
python "workloads/$workload.py"
@@ -0,0 +1,155 @@
import argparse
import numpy as np
import os
import random
import torch
import torch.nn as nn
from torch.utils.data import DataLoader, Subset
from torchvision.datasets import CIFAR10
import torchvision.transforms as transforms
import ray
from ray import tune
from ray.autoscaler._private.commands import kill_node
from ray.tune import CLIReporter
from ray.tune.ray_trial_executor import RayTrialExecutor
from ray.tune.schedulers import PopulationBasedTraining
from ray.tune.utils.util import merge_dicts
from ray.util.sgd.torch import TorchTrainer
from ray.util.sgd.torch.resnet import ResNet18
from ray.util.sgd.utils import BATCH_SIZE
parser = argparse.ArgumentParser()
parser.add_argument(
"--smoke-test",
action="store_true",
default=False,
help="Finish quickly for training.")
args = parser.parse_args()
class FailureInjectorExecutor(RayTrialExecutor):
"""Adds random failure injection to the TrialExecutor."""
def on_step_begin(self, trial_runner):
"""Before step(), update available resources and inject failure."""
self._update_avail_resources()
# With 10% probability inject failure to a worker.
if random.random() < 0.1 and not args.smoke_test:
# With 10% probability fully terminate the node.
should_terminate = random.random() < 0.1
kill_node(
"/home/ubuntu/ray_bootstrap_config.yaml",
yes=True,
hard=should_terminate,
override_cluster_name=None)
def initialization_hook():
# Need this for avoiding a connection restart issue on AWS.
os.environ["NCCL_SOCKET_IFNAME"] = "^docker0,lo"
os.environ["NCCL_LL_THRESHOLD"] = "0"
# set the below if needed
# print("NCCL DEBUG SET")
# os.environ["NCCL_DEBUG"] = "INFO"
def cifar_creator(config):
transform_train = transforms.Compose([
transforms.RandomCrop(32, padding=4),
transforms.RandomHorizontalFlip(),
transforms.ToTensor(),
transforms.Normalize((0.4914, 0.4822, 0.4465),
(0.2023, 0.1994, 0.2010)),
]) # meanstd transformation
transform_test = transforms.Compose([
transforms.ToTensor(),
transforms.Normalize((0.4914, 0.4822, 0.4465),
(0.2023, 0.1994, 0.2010)),
])
train_dataset = CIFAR10(
root="~/data", train=True, download=True, transform=transform_train)
validation_dataset = CIFAR10(
root="~/data", train=False, download=False, transform=transform_test)
if config.get("test_mode"):
train_dataset = Subset(train_dataset, list(range(64)))
validation_dataset = Subset(validation_dataset, list(range(64)))
train_loader = DataLoader(
train_dataset, batch_size=config[BATCH_SIZE], num_workers=2)
validation_loader = DataLoader(
validation_dataset, batch_size=config[BATCH_SIZE], num_workers=2)
return train_loader, validation_loader
def optimizer_creator(model, config):
"""Returns optimizer"""
return torch.optim.SGD(
model.parameters(),
lr=config.get("lr", 0.1),
momentum=config.get("momentum", 0.9))
ray.init(address="auto" if not args.smoke_test else None, _log_to_driver=True)
num_training_workers = 1 if args.smoke_test else 3
executor = FailureInjectorExecutor(queue_trials=True)
TorchTrainable = TorchTrainer.as_trainable(
model_creator=ResNet18,
data_creator=cifar_creator,
optimizer_creator=optimizer_creator,
loss_creator=nn.CrossEntropyLoss,
initialization_hook=initialization_hook,
num_workers=num_training_workers,
config={
"test_mode": args.smoke_test,
BATCH_SIZE: 128 * num_training_workers,
},
use_gpu=not args.smoke_test)
class NoFaultToleranceTrainable(TorchTrainable):
def _train(self):
train_stats = self.trainer.train(max_retries=0, profile=True)
validation_stats = self.trainer.validate(profile=True)
stats = merge_dicts(train_stats, validation_stats)
return stats
pbt_scheduler = PopulationBasedTraining(
time_attr="training_iteration",
metric="val_loss",
mode="min",
perturbation_interval=1,
hyperparam_mutations={
# distribution for resampling
"lr": lambda: np.random.uniform(0.001, 1),
# allow perturbations within this set of categorical values
"momentum": [0.8, 0.9, 0.99],
})
reporter = CLIReporter()
reporter.add_metric_column("val_loss", "loss")
reporter.add_metric_column("val_accuracy", "acc")
analysis = tune.run(
NoFaultToleranceTrainable,
num_samples=4,
config={
"lr": tune.choice([0.001, 0.01, 0.1]),
"momentum": 0.8,
"head_location": None,
"worker_locations": None
},
max_failures=-1, # used for fault tolerance
checkpoint_freq=2, # used for fault tolerance
progress_reporter=reporter,
scheduler=pbt_scheduler,
trial_executor=executor,
stop={"training_iteration": 1} if args.smoke_test else None)
print(analysis.get_best_config(metric="val_loss", mode="min"))
+1
View File
@@ -0,0 +1 @@
config_temporary.yaml
+53
View File
@@ -0,0 +1,53 @@
Long Running Tests
==================
This directory contains the long-running workloads which are intended to run
forever until they fail. To set up the project you need to run
.. code-block:: bash
$ pip install anyscale
$ anyscale init
Note that all the long running test is running inside virtual environment, tensorflow_p36
Running the Workloads
---------------------
Easiest approach is to use the `Anyscale UI <https://www.anyscale.dev/>`. First run ``anyscale snapshot create`` from the command line to create a project snapshot. Then from the UI, you can launch an individual session and execute the run command for each test.
You can also start the workloads using the CLI with:
.. code-block:: bash
$ anyscale start
$ anyscale run test_workload --workload=<WORKLOAD_NAME> --wheel=<RAY_WHEEL_LINK>
Doing this for each workload will start one EC2 instance per workload and will start the workloads
running (one per instance). A list of
available workload options is available in the `ray_projects/project.yaml` file.
Debugging
---------
The primary method to debug the test while it is running is to view the logs and the dashboard from the UI. After the test has failed, you can still view the stdout logs in the UI and also inspect
the logs under ``/tmp/ray/session*/logs/`` and
``/tmp/ray/session*/debug_state.txt``.
.. To check up on the workloads, run either
.. ``anyscale session --name="*" execute check-load``, which
.. will print the load on each machine, or
.. ``anyscale session --name="*" execute show-output``, which
.. will print the tail of the output for each workload.
Shut Down the Workloads
-----------------------
The instances running the workloads can all be killed by running
``anyscale stop <SESSION_NAME>``.
Adding a Workload
-----------------
To create a new workload, simply add a new Python file under ``workloads/`` and
add the workload in the run command in `ray-project/project.yaml`.
+48
View File
@@ -0,0 +1,48 @@
cluster_name: default
min_workers: 0
max_workers: 0
target_utilization_fraction: 0.8
idle_timeout_minutes: 5
# Cloud-provider specific configuration.
provider:
type: aws
region: us-west-2
availability_zone: us-west-2a
auth:
ssh_user: ubuntu
head_node:
InstanceType: m5.2xlarge
ImageId: ami-0888a3b5189309429 # DLAMI 7/1/19
BlockDeviceMappings:
- DeviceName: /dev/sda1
Ebs:
VolumeSize: 150
worker_nodes:
InstanceType: m5.large
ImageId: ami-0888a3b5189309429 # DLAMI 7/1/19
BlockDeviceMappings:
- DeviceName: /dev/sda1
Ebs:
VolumeSize: 150
# Run workers on spot by default. Comment this out to use on-demand.
InstanceMarketOptions:
MarketType: spot
# List of shell commands to run to set up nodes.
setup_commands: []
# Custom commands that will be run on the head node after common setup.
head_setup_commands: []
# Custom commands that will be run on worker nodes after common setup.
worker_setup_commands: []
# Command to start ray on the head node. You don't need to change this.
head_start_ray_commands: []
# Command to start ray on worker nodes. You don't need to change this.
worker_start_ray_commands: []
+66
View File
@@ -0,0 +1,66 @@
#!/usr/bin/env bash
ray_version=""
commit=""
ray_branch=""
workload=""
usage() {
echo "Start one microbenchmark trial."
}
for i in "$@"
do
echo "$i"
case "$i" in
--ray-version=*)
ray_version="${i#*=}"
;;
--commit=*)
commit="${i#*=}"
;;
--ray-branch=*)
ray_branch="${i#*=}"
;;
--workload=*)
workload="${i#*=}"
;;
--help)
usage
exit
;;
*)
echo "unknown arg, $i"
exit 1
;;
esac
done
if [[ $ray_version == "" || $commit == "" || $ray_branch == "" ]]
then
echo "Provide --ray-version, --commit, and --ray-branch"
exit 1
fi
echo "version: $ray_version"
echo "commit: $commit"
echo "branch: $ray_branch"
echo "workload: $workload"
wheel="https://s3-us-west-2.amazonaws.com/ray-wheels/$ray_branch/$commit/ray-$ray_version-cp36-cp36m-manylinux1_x86_64.whl"
echo set-window-option -g mouse on > ~/.tmux.conf
echo 'termcapinfo xterm* ti@:te@' > ~/.screenrc
# Serve load testing tool
rm -r wrk || true && git clone https://github.com/wg/wrk.git wrk && cd wrk && make -j && sudo cp wrk /usr/local/bin
pip install -U pip
unset RAY_ADDRESS
source activate tensorflow_p36
conda remove -y --force wrapt || true
pip install --upgrade pip
pip install -U tensorflow==1.14
pip install -q -U "$wheel" Click
pip install -q "ray[all]" "gym[atari]"
python "workloads/$workload.py"
@@ -0,0 +1,104 @@
# This workload tests repeatedly killing actors and submitting tasks to them.
import numpy as np
import sys
import time
import ray
from ray.cluster_utils import Cluster
num_redis_shards = 1
redis_max_memory = 10**8
object_store_memory = 10**8
num_nodes = 2
message = ("Make sure there is enough memory on this machine to run this "
"workload. We divide the system memory by 2 to provide a buffer.")
assert (num_nodes * object_store_memory + num_redis_shards * redis_max_memory <
ray.utils.get_system_memory() / 2), message
# Simulate a cluster on one machine.
cluster = Cluster()
for i in range(num_nodes):
cluster.add_node(
redis_port=6379 if i == 0 else None,
num_redis_shards=num_redis_shards if i == 0 else None,
num_cpus=8,
num_gpus=0,
resources={str(i): 2},
object_store_memory=object_store_memory,
redis_max_memory=redis_max_memory,
dashboard_host="0.0.0.0")
ray.init(address=cluster.address)
# Run the workload.
num_parents = 5
num_children = 5
death_probability = 0.95
@ray.remote
class Child(object):
def __init__(self, death_probability):
self.death_probability = death_probability
def ping(self):
# Exit process with some probability.
exit_chance = np.random.rand()
if exit_chance > self.death_probability:
sys.exit(-1)
@ray.remote
class Parent(object):
def __init__(self, num_children, death_probability):
self.death_probability = death_probability
self.children = [
Child.remote(death_probability) for _ in range(num_children)
]
def ping(self, num_pings):
children_outputs = []
for _ in range(num_pings):
children_outputs += [
child.ping.remote() for child in self.children
]
try:
ray.get(children_outputs)
except Exception:
# Replace the children if one of them died.
self.__init__(len(self.children), self.death_probability)
def kill(self):
# Clean up children.
ray.get([child.__ray_terminate__.remote() for child in self.children])
parents = [
Parent.remote(num_children, death_probability) for _ in range(num_parents)
]
iteration = 0
start_time = time.time()
previous_time = start_time
while True:
ray.get([parent.ping.remote(10) for parent in parents])
# Kill a parent actor with some probability.
exit_chance = np.random.rand()
if exit_chance > death_probability:
parent_index = np.random.randint(len(parents))
parents[parent_index].kill.remote()
parents[parent_index] = Parent.remote(num_children, death_probability)
new_time = time.time()
print("Iteration {}:\n"
" - Iteration time: {}.\n"
" - Absolute time: {}.\n"
" - Total elapsed time: {}.".format(
iteration, new_time - previous_time, new_time,
new_time - start_time))
previous_time = new_time
iteration += 1
@@ -0,0 +1,49 @@
# This workload tests running APEX
import ray
from ray.cluster_utils import Cluster
from ray.tune import run_experiments
num_redis_shards = 5
redis_max_memory = 10**8
object_store_memory = 10**9
num_nodes = 3
message = ("Make sure there is enough memory on this machine to run this "
"workload. We divide the system memory by 2 to provide a buffer.")
assert (num_nodes * object_store_memory + num_redis_shards * redis_max_memory <
ray.utils.get_system_memory() / 2), message
# Simulate a cluster on one machine.
cluster = Cluster()
for i in range(num_nodes):
cluster.add_node(
redis_port=6379 if i == 0 else None,
num_redis_shards=num_redis_shards if i == 0 else None,
num_cpus=20,
num_gpus=0,
resources={str(i): 2},
object_store_memory=object_store_memory,
redis_max_memory=redis_max_memory,
dashboard_host="0.0.0.0")
ray.init(address=cluster.address)
# Run the workload.
run_experiments({
"apex": {
"run": "APEX",
"env": "Pong-v0",
"config": {
"num_workers": 8,
"num_gpus": 0,
"buffer_size": 10000,
"learning_starts": 0,
"rollout_fragment_length": 1,
"train_batch_size": 1,
"min_iter_time_s": 10,
"timesteps_per_iteration": 10,
},
}
})
@@ -0,0 +1,48 @@
# This workload tests running IMPALA with remote envs
import ray
from ray.tune import run_experiments
from ray.cluster_utils import Cluster
num_redis_shards = 5
redis_max_memory = 10**8
object_store_memory = 10**8
num_nodes = 1
message = ("Make sure there is enough memory on this machine to run this "
"workload. We divide the system memory by 2 to provide a buffer.")
assert (num_nodes * object_store_memory + num_redis_shards * redis_max_memory <
ray.utils.get_system_memory() / 2), message
# Simulate a cluster on one machine.
cluster = Cluster()
for i in range(num_nodes):
cluster.add_node(
redis_port=6379 if i == 0 else None,
num_redis_shards=num_redis_shards if i == 0 else None,
num_cpus=10,
num_gpus=0,
resources={str(i): 2},
object_store_memory=object_store_memory,
redis_max_memory=redis_max_memory,
dashboard_host="0.0.0.0")
ray.init(address=cluster.address)
# Run the workload.
run_experiments({
"impala": {
"run": "IMPALA",
"env": "CartPole-v0",
"config": {
"num_workers": 8,
"num_gpus": 0,
"num_envs_per_worker": 5,
"remote_worker_envs": True,
"remote_env_batch_wait_ms": 99999999,
"rollout_fragment_length": 50,
"train_batch_size": 100,
},
},
})
@@ -0,0 +1,70 @@
# This workload tests submitting many actor methods.
import time
import numpy as np
import ray
from ray.cluster_utils import Cluster
num_redis_shards = 5
redis_max_memory = 10**8
object_store_memory = 10**8
num_nodes = 10
message = ("Make sure there is enough memory on this machine to run this "
"workload. We divide the system memory by 2 to provide a buffer.")
assert (num_nodes * object_store_memory + num_redis_shards * redis_max_memory <
ray.utils.get_system_memory() / 2), message
# Simulate a cluster on one machine.
cluster = Cluster()
for i in range(num_nodes):
cluster.add_node(
redis_port=6379 if i == 0 else None,
num_redis_shards=num_redis_shards if i == 0 else None,
num_cpus=5,
num_gpus=0,
resources={str(i): 2},
object_store_memory=object_store_memory,
redis_max_memory=redis_max_memory,
dashboard_host="0.0.0.0")
ray.init(address=cluster.address)
# Run the workload.
@ray.remote
class Actor(object):
def __init__(self):
self.value = 0
def method(self):
self.value += 1
return np.zeros(1024, dtype=np.uint8)
actors = [
Actor._remote([], {}, num_cpus=0.1, resources={str(i % num_nodes): 0.1})
for i in range(num_nodes * 5)
]
iteration = 0
start_time = time.time()
previous_time = start_time
while True:
for _ in range(100):
previous_ids = [a.method.remote() for a in actors]
ray.get(previous_ids)
new_time = time.time()
print("Iteration {}:\n"
" - Iteration time: {}.\n"
" - Absolute time: {}.\n"
" - Total elapsed time: {}.".format(
iteration, new_time - previous_time, new_time,
new_time - start_time))
previous_time = new_time
iteration += 1
@@ -0,0 +1,102 @@
# This workload tests many drivers using the same cluster.
import time
import ray
from ray.cluster_utils import Cluster
from ray.test_utils import run_string_as_driver
num_redis_shards = 5
redis_max_memory = 10**8
object_store_memory = 10**8
num_nodes = 4
message = ("Make sure there is enough memory on this machine to run this "
"workload. We divide the system memory by 2 to provide a buffer.")
assert (num_nodes * object_store_memory + num_redis_shards * redis_max_memory <
ray.utils.get_system_memory() / 2), message
# Simulate a cluster on one machine.
cluster = Cluster()
for i in range(num_nodes):
cluster.add_node(
redis_port=6379 if i == 0 else None,
num_redis_shards=num_redis_shards if i == 0 else None,
num_cpus=4,
num_gpus=0,
resources={str(i): 5},
object_store_memory=object_store_memory,
redis_max_memory=redis_max_memory,
dashboard_host="0.0.0.0")
ray.init(address=cluster.address)
# Run the workload.
# Define a driver script that runs a few tasks and actors on each node in the
# cluster.
driver_script = """
import ray
ray.init(address="{}")
num_nodes = {}
@ray.remote
def f():
return 1
@ray.remote
class Actor(object):
def method(self):
return 1
for _ in range(5):
for i in range(num_nodes):
assert (ray.get(
f._remote(args=[], kwargs={{}}, resources={{str(i): 1}})) == 1)
actor = Actor._remote(args=[], kwargs={{}}, resources={{str(i): 1}})
assert ray.get(actor.method.remote()) == 1
print("success")
""".format(cluster.address, num_nodes)
@ray.remote
def run_driver():
output = run_string_as_driver(driver_script)
assert "success" in output
iteration = 0
running_ids = [
run_driver._remote(
args=[], kwargs={}, num_cpus=0, resources={str(i): 0.01})
for i in range(num_nodes)
]
start_time = time.time()
previous_time = start_time
while True:
# Wait for a driver to finish and start a new driver.
[ready_id], running_ids = ray.wait(running_ids, num_returns=1)
ray.get(ready_id)
running_ids.append(
run_driver._remote(
args=[],
kwargs={},
num_cpus=0,
resources={str(iteration % num_nodes): 0.01}))
new_time = time.time()
print("Iteration {}:\n"
" - Iteration time: {}.\n"
" - Absolute time: {}.\n"
" - Total elapsed time: {}.".format(
iteration, new_time - previous_time, new_time,
new_time - start_time))
previous_time = new_time
iteration += 1
@@ -0,0 +1,66 @@
# This workload tests submitting and getting many tasks over and over.
import time
import numpy as np
import ray
from ray.cluster_utils import Cluster
num_redis_shards = 5
redis_max_memory = 10**8
object_store_memory = 10**8
num_nodes = 10
message = ("Make sure there is enough memory on this machine to run this "
"workload. We divide the system memory by 2 to provide a buffer.")
assert (num_nodes * object_store_memory + num_redis_shards * redis_max_memory <
ray.utils.get_system_memory() / 2), message
# Simulate a cluster on one machine.
cluster = Cluster()
for i in range(num_nodes):
cluster.add_node(
redis_port=6379 if i == 0 else None,
num_redis_shards=num_redis_shards if i == 0 else None,
num_cpus=2,
num_gpus=0,
resources={str(i): 2},
object_store_memory=object_store_memory,
redis_max_memory=redis_max_memory,
dashboard_host="0.0.0.0")
ray.init(address=cluster.address)
# Run the workload.
@ray.remote
def f(*xs):
return np.zeros(1024, dtype=np.uint8)
iteration = 0
ids = []
start_time = time.time()
previous_time = start_time
while True:
for _ in range(50):
new_constrained_ids = [
f._remote(args=[*ids], resources={str(i % num_nodes): 1})
for i in range(25)
]
new_unconstrained_ids = [f.remote(*ids) for _ in range(25)]
ids = new_constrained_ids + new_unconstrained_ids
ray.get(ids)
new_time = time.time()
print("Iteration {}:\n"
" - Iteration time: {}.\n"
" - Absolute time: {}.\n"
" - Total elapsed time: {}.".format(
iteration, new_time - previous_time, new_time,
new_time - start_time))
previous_time = new_time
iteration += 1
@@ -0,0 +1,93 @@
# This workload stresses distributed reference counting by passing and
# returning serialized ObjectRefs.
import time
import random
import numpy as np
import ray
from ray.cluster_utils import Cluster
num_redis_shards = 5
redis_max_memory = 10**8
object_store_memory = 10**8
num_nodes = 10
message = ("Make sure there is enough memory on this machine to run this "
"workload. We divide the system memory by 2 to provide a buffer.")
assert (num_nodes * object_store_memory + num_redis_shards * redis_max_memory <
ray.utils.get_system_memory() / 2), message
# Simulate a cluster on one machine.
cluster = Cluster()
for i in range(num_nodes):
cluster.add_node(
redis_port=6379 if i == 0 else None,
num_redis_shards=num_redis_shards if i == 0 else None,
num_cpus=2,
num_gpus=0,
resources={str(i): 2},
object_store_memory=object_store_memory,
redis_max_memory=redis_max_memory,
dashboard_host="0.0.0.0")
ray.init(address=cluster.address)
# Run the workload.
@ray.remote(max_retries=0)
def churn():
return ray.put(np.zeros(1024 * 1024, dtype=np.uint8))
@ray.remote(max_retries=0)
def child(*xs):
obj_ref = ray.put(np.zeros(1024 * 1024, dtype=np.uint8))
return obj_ref
@ray.remote(max_retries=0)
def f(*xs):
if xs:
return random.choice(xs)
else:
return child.remote(*xs)
iteration = 0
ids = []
start_time = time.time()
previous_time = start_time
while True:
for _ in range(50):
new_constrained_ids = [
f._remote(args=ids, resources={str(i % num_nodes): 1})
for i in range(25)
]
new_unconstrained_ids = [f.remote(*ids) for _ in range(25)]
ids = new_constrained_ids + new_unconstrained_ids
# Fill the object store while the tasks are running.
for i in range(num_nodes):
for _ in range(10):
[
churn._remote(args=[], resources={str(i % num_nodes): 1})
for _ in range(10)
]
# Make sure that the objects are still available.
child_ids = ray.get(ids)
for child_id in child_ids:
ray.get(child_id)
new_time = time.time()
print("Iteration {}:\n"
" - Iteration time: {}.\n"
" - Absolute time: {}.\n"
" - Total elapsed time: {}.".format(
iteration, new_time - previous_time, new_time,
new_time - start_time))
previous_time = new_time
iteration += 1
@@ -0,0 +1,68 @@
# This workload tests repeatedly killing a node and adding a new node.
import time
import ray
from ray.cluster_utils import Cluster
from ray.test_utils import get_other_nodes
num_redis_shards = 5
redis_max_memory = 10**8
object_store_memory = 10**8
num_nodes = 10
message = ("Make sure there is enough memory on this machine to run this "
"workload. We divide the system memory by 2 to provide a buffer.")
assert (num_nodes * object_store_memory + num_redis_shards * redis_max_memory <
ray.utils.get_system_memory() / 2), message
# Simulate a cluster on one machine.
cluster = Cluster()
for i in range(num_nodes):
cluster.add_node(
redis_port=6379 if i == 0 else None,
num_redis_shards=num_redis_shards if i == 0 else None,
num_cpus=2,
num_gpus=0,
resources={str(i): 2},
object_store_memory=object_store_memory,
redis_max_memory=redis_max_memory,
dashboard_host="0.0.0.0")
ray.init(address=cluster.address)
# Run the workload.
@ray.remote
def f(*xs):
return 1
iteration = 0
previous_ids = [1 for _ in range(100)]
start_time = time.time()
previous_time = start_time
while True:
for _ in range(100):
previous_ids = [f.remote(previous_id) for previous_id in previous_ids]
ray.get(previous_ids)
for _ in range(100):
previous_ids = [f.remote(previous_id) for previous_id in previous_ids]
node_to_kill = get_other_nodes(cluster, exclude_head=True)[0]
# Remove the first non-head node.
cluster.remove_node(node_to_kill)
cluster.add_node()
new_time = time.time()
print("Iteration {}:\n"
" - Iteration time: {}.\n"
" - Absolute time: {}.\n"
" - Total elapsed time: {}.".format(
iteration, new_time - previous_time, new_time,
new_time - start_time))
previous_time = new_time
iteration += 1
@@ -0,0 +1,56 @@
# This workload tests running PBT
import ray
from ray.tune import run_experiments
from ray.tune.schedulers import PopulationBasedTraining
from ray.cluster_utils import Cluster
num_redis_shards = 5
redis_max_memory = 10**8
object_store_memory = 10**8
num_nodes = 3
message = ("Make sure there is enough memory on this machine to run this "
"workload. We divide the system memory by 2 to provide a buffer.")
assert (num_nodes * object_store_memory + num_redis_shards * redis_max_memory <
ray.utils.get_system_memory() / 2), message
# Simulate a cluster on one machine.
cluster = Cluster()
for i in range(num_nodes):
cluster.add_node(
redis_port=6379 if i == 0 else None,
num_redis_shards=num_redis_shards if i == 0 else None,
num_cpus=10,
num_gpus=0,
resources={str(i): 2},
object_store_memory=object_store_memory,
redis_max_memory=redis_max_memory,
dashboard_host="0.0.0.0")
ray.init(address=cluster.address)
# Run the workload.
pbt = PopulationBasedTraining(
time_attr="training_iteration",
metric="episode_reward_mean",
mode="max",
perturbation_interval=10,
hyperparam_mutations={
"lr": [0.1, 0.01, 0.001, 0.0001],
})
run_experiments(
{
"pbt_test": {
"run": "PG",
"env": "CartPole-v0",
"num_samples": 8,
"config": {
"lr": 0.01,
},
}
},
scheduler=pbt,
verbose=False)
@@ -0,0 +1,66 @@
import time
import subprocess
from subprocess import PIPE
import requests
import ray
from ray import serve
from ray.cluster_utils import Cluster
num_redis_shards = 1
redis_max_memory = 10**8
object_store_memory = 10**8
num_nodes = 5
cluster = Cluster()
for i in range(num_nodes):
cluster.add_node(
redis_port=6379 if i == 0 else None,
num_redis_shards=num_redis_shards if i == 0 else None,
num_cpus=8,
num_gpus=0,
resources={str(i): 2},
object_store_memory=object_store_memory,
redis_max_memory=redis_max_memory,
dashboard_host="0.0.0.0")
ray.init(address=cluster.address, dashboard_host="0.0.0.0")
client = serve.start()
@serve.accept_batch
def echo(_):
time.sleep(0.01) # Sleep for 10ms
ray.show_in_dashboard(
str(serve.context.batch_size), key="Current batch size")
return ["hi {}".format(i) for i in range(serve.context.batch_size)]
config = {"num_replicas": 30, "max_batch_size": 16}
client.create_backend("echo:v1", echo, config=config)
client.create_endpoint("echo", backend="echo:v1", route="/echo")
print("Warming up")
for _ in range(5):
resp = requests.get("http://127.0.0.1:8000/echo").text
print(resp)
time.sleep(0.5)
connections = int(config["num_replicas"] * config["max_batch_size"] * 0.75)
num_threads = 2
time_to_run = "60m"
while True:
proc = subprocess.Popen(
[
"wrk", "-c",
str(connections), "-t",
str(num_threads), "-s", time_to_run, "http://127.0.0.1:8000/echo"
],
stdout=PIPE,
stderr=PIPE)
print("started load testing")
proc.wait()
out, err = proc.communicate()
print(out.decode())
print(err.decode())
@@ -0,0 +1,121 @@
import random
import string
import time
import requests
import ray
from ray import serve
from ray.cluster_utils import Cluster
num_redis_shards = 1
redis_max_memory = 10**8
object_store_memory = 10**8
num_nodes = 5
cpus_per_node = 2
cluster = Cluster()
for i in range(num_nodes):
cluster.add_node(
redis_port=6379 if i == 0 else None,
num_redis_shards=num_redis_shards if i == 0 else None,
num_cpus=2,
num_gpus=0,
resources={str(i): 2},
object_store_memory=object_store_memory,
redis_max_memory=redis_max_memory,
dashboard_host="0.0.0.0")
ray.init(
address=cluster.address, dashboard_host="0.0.0.0", log_to_driver=False)
client = serve.start(detached=True)
@ray.remote
class RandomKiller:
def __init__(self, client, kill_period_s=1):
self.client = client
self.kill_period_s = kill_period_s
def _get_all_serve_actors(self):
controller = self.client._controller
routers = list(ray.get(controller.get_routers.remote()).values())
all_handles = routers + [controller]
worker_handle_dict = ray.get(
controller.get_all_worker_handles.remote())
for _, replica_dict in worker_handle_dict.items():
all_handles.extend(list(replica_dict.values()))
return all_handles
def run(self):
while True:
ray.kill(
random.choice(self._get_all_serve_actors()), no_restart=False)
time.sleep(self.kill_period_s)
class RandomTest:
def __init__(self, client, max_endpoints=1):
self.client = client
self.max_endpoints = max_endpoints
self.weighted_actions = [
(self.create_endpoint, 1),
(self.verify_endpoint, 4),
]
self.endpoints = []
for _ in range(max_endpoints):
self.create_endpoint()
def create_endpoint(self):
if len(self.endpoints) == self.max_endpoints:
endpoint_to_delete = self.endpoints.pop()
self.client.delete_endpoint(endpoint_to_delete)
self.client.delete_backend(endpoint_to_delete)
new_endpoint = "".join(
[random.choice(string.ascii_letters) for _ in range(10)])
def handler(self, *args):
return new_endpoint
self.client.create_backend(new_endpoint, handler)
self.client.create_endpoint(
new_endpoint, backend=new_endpoint, route="/" + new_endpoint)
self.endpoints.append(new_endpoint)
def verify_endpoint(self):
endpoint = random.choice(self.endpoints)
for _ in range(100):
try:
r = requests.get("http://127.0.0.1:8000/" + endpoint)
assert r.text == endpoint
except Exception:
print("Request to {} failed.".format(endpoint))
time.sleep(0.01)
def run(self):
iteration = 0
start_time = time.time()
previous_time = start_time
while True:
for _ in range(100):
actions, weights = zip(*self.weighted_actions)
random.choices(actions, weights=weights)[0]()
new_time = time.time()
print("Iteration {}:\n"
" - Iteration time: {}.\n"
" - Absolute time: {}.\n"
" - Total elapsed time: {}.".format(
iteration, new_time - previous_time, new_time,
new_time - start_time))
previous_time = new_time
iteration += 1
random_killer = RandomKiller.remote(client)
random_killer.run.remote()
# Subtract 4 from the CPUs available for master, router, HTTP proxy,
# and metric monitor actors.
RandomTest(client, max_endpoints=(num_nodes * cpus_per_node) - 4).run()
+57
View File
@@ -0,0 +1,57 @@
cluster_name: ray-release-microbenchmark
min_workers: 0
max_workers: 0
target_utilization_fraction: 0.8
idle_timeout_minutes: 5
# Cloud-provider specific configuration.
provider:
type: aws
region: us-west-2
availability_zone: us-west-2a
auth:
ssh_user: ubuntu
head_node:
InstanceType: m4.16xlarge
ImageId: ami-06d51e91cea0dac8d # Ubuntu 18.04
BlockDeviceMappings:
- DeviceName: /dev/sda1
Ebs:
VolumeSize: 150
worker_nodes:
InstanceType: m5.large
ImageId: ami-06d51e91cea0dac8d # Ubuntu 18.04
BlockDeviceMappings:
- DeviceName: /dev/sda1
Ebs:
VolumeSize: 150
# Run workers on spot by default. Comment this out to use on-demand.
InstanceMarketOptions:
MarketType: spot
# List of shell commands to run to set up nodes.
setup_commands:
# Install latest TensorFlow
- echo set-window-option -g mouse on > ~/.tmux.conf
- echo 'termcapinfo xterm* ti@:te@' > ~/.screenrc
# Custom commands that will be run on the head node after common setup.
head_setup_commands:
# Install Anaconda.
- wget --quiet https://repo.continuum.io/archive/Anaconda3-5.0.1-Linux-x86_64.sh || true
- bash Anaconda3-5.0.1-Linux-x86_64.sh -b -p $HOME/anaconda3 || true
- echo 'export PATH="$HOME/anaconda3/bin:$PATH"' >> ~/.bashrc
- pip install -U pip
- conda uninstall -y terminado
# Custom commands that will be run on worker nodes after common setup.
worker_setup_commands: []
# Command to start ray on the head node. You don't need to change this.
head_start_ray_commands: []
# Command to start ray on worker nodes. You don't need to change this.
worker_start_ray_commands: []
+60
View File
@@ -0,0 +1,60 @@
#!/usr/bin/env bash
ray_version=""
commit=""
ray_branch=""
usage() {
echo "Start one microbenchmark trial."
}
for i in "$@"
do
case "$i" in
--ray-version=*)
ray_version="${i#*=}"
;;
--commit=*)
commit="${i#*=}"
;;
--ray-branch=*)
ray_branch="${i#*=}"
;;
--workload=*)
workload="${i#*=}"
;;
--help)
usage
exit
;;
*)
echo "unknown arg, $2"
exit 1
;;
esac
done
if [ -z "$ray_version" ] || [ -z "$commit" ] || [ -z "$ray_branch" ]
then
echo "Provide --ray-version, --commit, and --ray-branch"
exit 1
fi
echo "version: $ray_version"
echo "commit: $commit"
echo "branch: $ray_branch"
echo "workload: $workload"
wheel="https://s3-us-west-2.amazonaws.com/ray-wheels/$ray_branch/$commit/ray-$ray_version-cp38-cp38-manylinux1_x86_64.whl"
echo set-window-option -g mouse on > ~/.tmux.conf
echo 'termcapinfo xterm* ti@:te@' > ~/.screenrc
pip uninstall -y -q ray
pip install --upgrade pip
pip install -U "$wheel"
unset RAY_ADDRESS
OMP_NUM_THREADS=64 ray microbenchmark
@@ -0,0 +1,39 @@
cluster_name: ray-rllib-regression-tests
min_workers: 0
max_workers: 0
# Cloud-provider specific configuration.
provider:
type: aws
region: us-west-2
availability_zone: us-west-2a
cache_stopped_nodes: False
# How Ray will authenticate with newly launched nodes.
auth:
ssh_user: ubuntu
head_node:
InstanceType: p3.16xlarge
ImageId: ami-07728e9e2742b0662 # Deep Learning AMI (Ubuntu 16.04)
# Set primary volume to 25 GiB
BlockDeviceMappings:
- DeviceName: /dev/sda1
Ebs:
VolumeSize: 100
# List of shell commands to run to set up nodes.
setup_commands: []
# Command to start ray on the head node. You don't need to change this.
head_start_ray_commands:
- source activate tensorflow_p36 && ray stop
- ulimit -n 65536; source activate tensorflow_p36 && OMP_NUM_THREADS=1 ray start --head --port=6379 --object-manager-port=8076 --autoscaling-config=~/ray_bootstrap_config.yaml
# Command to start ray on worker nodes. You don't need to change this.
worker_start_ray_commands:
- source activate tensorflow_p36 && ray stop
- ulimit -n 65536; source activate tensorflow_p36 && OMP_NUM_THREADS=1 ray start --address=$RAY_HEAD_IP:6379 --object-manager-port=8076
@@ -0,0 +1,145 @@
# This file runs on a single g3.16xl or p3.16xl node. It is suggested
# to run these in a DLAMI / tensorflow_p36 env. Note that RL runs are
# inherently high variance, so you'll have to check to see if the
# rewards reached seem reasonably in line with previous results.
#
# You can find the reference results here:
# https://github.com/ray-project/ray/tree/master/doc/dev/release_logs
atari-impala:
env: BreakoutNoFrameskip-v4
run: IMPALA
num_samples: 4
stop:
time_total_s: 3600
config:
rollout_fragment_length: 50
train_batch_size: 500
num_workers: 10
num_envs_per_worker: 5
clip_rewards: True
lr_schedule: [
[0, 0.0005],
[20000000, 0.000000000001],
]
num_gpus: 1
atari-ppo-tf:
env: BreakoutNoFrameskip-v4
run: PPO
num_samples: 4
stop:
time_total_s: 3600
config:
lambda: 0.95
kl_coeff: 0.5
clip_rewards: True
clip_param: 0.1
vf_clip_param: 10.0
entropy_coeff: 0.01
train_batch_size: 5000
rollout_fragment_length: 100
sgd_minibatch_size: 500
num_sgd_iter: 10
num_workers: 10
num_envs_per_worker: 5
batch_mode: truncate_episodes
observation_filter: NoFilter
vf_share_layers: true
num_gpus: 1
atari-ppo-torch:
env: BreakoutNoFrameskip-v4
run: PPO
num_samples: 4
stop:
time_total_s: 3600
config:
framework: torch
lambda: 0.95
kl_coeff: 0.5
clip_rewards: True
clip_param: 0.1
vf_clip_param: 10.0
entropy_coeff: 0.01
train_batch_size: 5000
rollout_fragment_length: 100
sgd_minibatch_size: 500
num_sgd_iter: 10
num_workers: 10
num_envs_per_worker: 5
batch_mode: truncate_episodes
observation_filter: NoFilter
vf_share_layers: true
num_gpus: 1
apex:
env: BreakoutNoFrameskip-v4
run: APEX
num_samples: 4
stop:
time_total_s: 3600
config:
double_q: false
dueling: false
num_atoms: 1
noisy: false
n_step: 3
lr: .0001
adam_epsilon: .00015
hiddens: [512]
buffer_size: 1000000
exploration_config:
epsilon_timesteps: 200000
final_epsilon: 0.01
prioritized_replay_alpha: 0.5
final_prioritized_replay_beta: 1.0
prioritized_replay_beta_annealing_timesteps: 2000000
num_gpus: 1
num_workers: 8
num_envs_per_worker: 8
rollout_fragment_length: 20
train_batch_size: 512
target_network_update_freq: 50000
timesteps_per_iteration: 25000
atari-a2c:
env: BreakoutNoFrameskip-v4
run: A2C
num_samples: 4
stop:
time_total_s: 3600
config:
rollout_fragment_length: 20
clip_rewards: True
num_workers: 5
num_envs_per_worker: 5
num_gpus: 1
lr_schedule: [
[0, 0.0007],
[20000000, 0.000000000001],
]
atari-basic-dqn:
env: BreakoutNoFrameskip-v4
run: DQN
num_samples: 4
stop:
time_total_s: 3600
config:
double_q: false
dueling: false
num_atoms: 1
noisy: false
prioritized_replay: false
n_step: 1
target_network_update_freq: 8000
lr: .0000625
adam_epsilon: .00015
hiddens: [512]
learning_starts: 20000
buffer_size: 1000000
rollout_fragment_length: 4
train_batch_size: 32
exploration_config:
epsilon_timesteps: 200000
final_epsilon: 0.01
prioritized_replay_alpha: 0.5
final_prioritized_replay_beta: 1.0
prioritized_replay_beta_annealing_timesteps: 2000000
num_gpus: 0.2
timesteps_per_iteration: 10000
@@ -0,0 +1 @@
ray[rllib]
+52
View File
@@ -0,0 +1,52 @@
#!/usr/bin/env bash
ray_version=""
commit=""
ray_branch=""
for i in "$@"
do
echo "$i"
case "$i" in
--ray-version=*)
ray_version="${i#*=}"
;;
--commit=*)
commit="${i#*=}"
;;
--ray-branch=*)
ray_branch="${i#*=}"
;;
--workload=*)
;;
--help)
usage
exit
;;
*)
echo "unknown arg, $i"
exit 1
;;
esac
done
if [[ $ray_version == "" || $commit == "" || $ray_branch == "" ]]
then
echo "Provide --ray-version, --commit, and --ray-branch"
exit 1
fi
echo "version: $ray_version"
echo "commit: $commit"
echo "branch: $ray_branch"
echo "workload: ignored"
wheel="https://s3-us-west-2.amazonaws.com/ray-wheels/$ray_branch/$commit/ray-$ray_version-cp36-cp36m-manylinux1_x86_64.whl"
conda uninstall -y terminado
source activate tensorflow_p36 && pip install -U pip
source activate tensorflow_p36 && pip install -U "$wheel"
source activate tensorflow_p36 && pip install "ray[rllib]" "ray[debug]"
source activate tensorflow_p36 && pip install torch==1.6 torchvision
source activate tensorflow_p36 && pip install boto3==1.4.8 cython==0.29.0
source activate tensorflow_p36 && rllib train -f compact-regression-test.yaml
@@ -0,0 +1,23 @@
# Taken from rllib/tuned_examples/atari_impala_large.yaml
# Runs on a g3.16xl node with 5 m5.24xl workers
# Takes roughly 10 minutes. x10?
atari-impala:
env:
grid_search:
- BreakoutNoFrameskip-v4
- BeamRiderNoFrameskip-v4
- QbertNoFrameskip-v4
- SpaceInvadersNoFrameskip-v4
run: IMPALA
stop:
timesteps_total: 30000000
config:
rollout_fragment_length: 50
train_batch_size: 500
num_workers: 128
num_envs_per_worker: 5
clip_rewards: True
lr_schedule: [
[0, 0.0005],
[20000000, 0.000000000001],
]
@@ -0,0 +1,105 @@
####################################################################
# All nodes in this cluster will auto-terminate in 1 hour
####################################################################
# An unique identifier for the head node and workers of this cluster.
cluster_name: ray-rllib-stress-tests
# The minimum number of workers nodes to launch in addition to the head
# node. This number should be >= 0.
min_workers: 9
# The maximum number of workers nodes to launch in addition to the head
# node. This takes precedence over min_workers.
max_workers: 9
# The autoscaler will scale up the cluster to this target fraction of resource
# usage. For example, if a cluster of 10 nodes is 100% busy and
# target_utilization is 0.8, it would resize the cluster to 13. This fraction
# can be decreased to increase the aggressiveness of upscaling.
# This value must be less than 1.0 for scaling to happen.
target_utilization_fraction: 0.8
# If a node is idle for this many minutes, it will be removed.
idle_timeout_minutes: 5
# Cloud-provider specific configuration.
provider:
type: aws
region: us-west-2
availability_zone: us-west-2a
cache_stopped_nodes: False
# How Ray will authenticate with newly launched nodes.
auth:
ssh_user: ubuntu
# By default Ray creates a new private keypair, but you can also use your own.
# If you do so, make sure to also set "KeyName" in the head and worker node
# configurations below.
# ssh_private_key: /path/to/your/key.pem
# Provider-specific config for the head node, e.g. instance type. By default
# Ray will auto-configure unspecified fields such as SubnetId and KeyName.
# For more documentation on available fields, see:
# http://boto3.readthedocs.io/en/latest/reference/services/ec2.html#EC2.ServiceResource.create_instances
head_node:
InstanceType: p3.16xlarge
ImageId: ami-07728e9e2742b0662 # Deep Learning AMI (Ubuntu 16.04)
# Set primary volume to 25 GiB
BlockDeviceMappings:
- DeviceName: /dev/sda1
Ebs:
VolumeSize: 100
# Additional options in the boto docs.
# Provider-specific config for worker nodes, e.g. instance type. By default
# Ray will auto-configure unspecified fields such as SubnetId and KeyName.
# For more documentation on available fields, see:
# http://boto3.readthedocs.io/en/latest/reference/services/ec2.html#EC2.ServiceResource.create_instances
worker_nodes:
InstanceType: m4.16xlarge
ImageId: ami-07728e9e2742b0662 # Deep Learning AMI (Ubuntu 16.04)
# Set primary volume to 25 GiB
BlockDeviceMappings:
- DeviceName: /dev/sda1
Ebs:
VolumeSize: 100
# Run workers on spot by default. Comment this out to use on-demand.
# InstanceMarketOptions:
# MarketType: spot
# Additional options can be found in the boto docs, e.g.
# SpotOptions:
# MaxPrice: MAX_HOURLY_PRICE
# Additional options in the boto docs.
# Files or directories to copy to the head and worker nodes. The format is a
# dictionary from REMOTE_PATH: LOCAL_PATH, e.g.
file_mounts: {
# "/path1/on/remote/machine": "/path1/on/local/machine",
# "/path2/on/remote/machine": "/path2/on/local/machine",
}
# List of shell commands to run to set up nodes.
setup_commands: []
# Custom commands that will be run on the head node after common setup.
head_setup_commands: []
# Custom commands that will be run on worker nodes after common setup.
worker_setup_commands: []
# Command to start ray on the head node. You don't need to change this.
head_start_ray_commands:
- source activate tensorflow_p36 && ray stop
- ulimit -n 65536; source activate tensorflow_p36 && OMP_NUM_THREADS=1 ray start --head --port=6379 --object-manager-port=8076 --autoscaling-config=~/ray_bootstrap_config.yaml
# Command to start ray on worker nodes. You don't need to change this.
worker_start_ray_commands:
- source activate tensorflow_p36 && ray stop
- ulimit -n 65536; source activate tensorflow_p36 && OMP_NUM_THREADS=1 ray start --address=$RAY_HEAD_IP:6379 --object-manager-port=8076
@@ -0,0 +1 @@
ray[rllib]
+56
View File
@@ -0,0 +1,56 @@
#!/usr/bin/env bash
ray_version=""
commit=""
ray_branch=""
for i in "$@"
do
echo "$i"
case "$i" in
--ray-version=*)
ray_version="${i#*=}"
;;
--commit=*)
commit="${i#*=}"
;;
--ray-branch=*)
ray_branch="${i#*=}"
;;
--workload=*)
;;
--help)
usage
exit
;;
*)
echo "unknown arg, $i"
exit 1
;;
esac
done
if [[ $ray_version == "" || $commit == "" || $ray_branch == "" ]]
then
echo "Provide --ray-version, --commit, and --ray-branch"
exit 1
fi
echo "version: $ray_version"
echo "commit: $commit"
echo "branch: $ray_branch"
echo "workload: ignored"
wheel="https://s3-us-west-2.amazonaws.com/ray-wheels/$ray_branch/$commit/ray-$ray_version-cp36-cp36m-manylinux1_x86_64.whl"
conda uninstall -y terminado
source activate tensorflow_p36 && pip install -U pip
source activate tensorflow_p36 && pip install -U "$wheel"
source activate tensorflow_p36 && pip install "ray[rllib]" "ray[debug]"
source activate tensorflow_p36 && pip install boto3==1.4.8 cython==0.29.0
source activate tensorflow_p36
python3 wait_cluster.py
rllib train -f atari_impala_xlarge.yaml --ray-address=auto --queue-trials
@@ -0,0 +1,10 @@
import ray
import time
ray.init(address="auto")
curr_nodes = 0
while not curr_nodes > 8:
print("Waiting for more nodes to come up: {}/{}".format(curr_nodes, 8))
curr_nodes = len(ray.nodes())
time.sleep(5)
@@ -0,0 +1,39 @@
cluster_name: ray-rllib-regression-tests
min_workers: 0
max_workers: 0
# Cloud-provider specific configuration.
provider:
type: aws
region: us-west-2
availability_zone: us-west-2a
cache_stopped_nodes: False
# How Ray will authenticate with newly launched nodes.
auth:
ssh_user: ubuntu
head_node:
InstanceType: p2.xlarge # Cheaper 1GPU K80 instance
ImageId: ami-07728e9e2742b0662 # Deep Learning AMI (Ubuntu 16.04)
# Set primary volume to 25 GiB
BlockDeviceMappings:
- DeviceName: /dev/sda1
Ebs:
VolumeSize: 100
# List of shell commands to run to set up nodes.
setup_commands: []
# Command to start ray on the head node. You don't need to change this.
head_start_ray_commands:
- source activate tensorflow_p36 && ray stop
- ulimit -n 65536; source activate tensorflow_p36 && OMP_NUM_THREADS=1 ray start --head --redis-port=6379 --object-manager-port=8076 --autoscaling-config=~/ray_bootstrap_config.yaml
# Command to start ray on worker nodes. You don't need to change this.
worker_start_ray_commands:
- source activate tensorflow_p36 && ray stop
- ulimit -n 65536; source activate tensorflow_p36 && OMP_NUM_THREADS=1 ray start --address=$RAY_HEAD_IP:6379 --object-manager-port=8076
@@ -0,0 +1,6 @@
ray[rllib]
ray[debug]
torch==1.6+cu101
torchvision==0.7.0+cu101
boto3==1.4.8
cython==0.29.0
+53
View File
@@ -0,0 +1,53 @@
#!/usr/bin/env bash
ray_version=""
commit=""
ray_branch=""
for i in "$@"
do
echo "$i"
case "$i" in
--ray-version=*)
ray_version="${i#*=}"
;;
--commit=*)
commit="${i#*=}"
;;
--ray-branch=*)
ray_branch="${i#*=}"
;;
--workload=*)
;;
--help)
usage
exit
;;
*)
echo "unknown arg, $i"
exit 1
;;
esac
done
if [[ $ray_version == "" || $commit == "" || $ray_branch == "" ]]
then
echo "Provide --ray-version, --commit, and --ray-branch"
exit 1
fi
echo "version: $ray_version"
echo "commit: $commit"
echo "branch: $ray_branch"
echo "workload: ignored"
wheel="https://s3-us-west-2.amazonaws.com/ray-wheels/$ray_branch/$commit/ray-$ray_version-cp36-cp36m-manylinux1_x86_64.whl"
conda uninstall -y terminado
source activate tensorflow_p36 && pip install -U pip
source activate tensorflow_p36 && pip install -U "$wheel"
# Run all test cases, but with a forced num_gpus=1.
# TODO: (sven) chose correct dir and run over all RLlib tests and example scripts!
source activate tensorflow_p36 && export RAY_FORCE_NUM_GPUS=1 && cd ~ && python -m pytest test_attention_net_learning.py
+120
View File
@@ -0,0 +1,120 @@
####################################################################
# All nodes in this cluster will auto-terminate in 1 hour
####################################################################
# An unique identifier for the head node and workers of this cluster.
cluster_name: ray-stress-tests
# The minimum number of workers nodes to launch in addition to the head
# node. This number should be >= 0.
min_workers: 105
# The maximum number of workers nodes to launch in addition to the head
# node. This takes precedence over min_workers.
max_workers: 105
# The autoscaler will scale up the cluster to this target fraction of resource
# usage. For example, if a cluster of 10 nodes is 100% busy and
# target_utilization is 0.8, it would resize the cluster to 13. This fraction
# can be decreased to increase the aggressiveness of upscaling.
# This value must be less than 1.0 for scaling to happen.
target_utilization_fraction: 0.8
# If a node is idle for this many minutes, it will be removed.
idle_timeout_minutes: 5
# Cloud-provider specific configuration.
provider:
type: aws
region: us-west-2
availability_zone: us-west-2a
cache_stopped_nodes: False
# How Ray will authenticate with newly launched nodes.
auth:
ssh_user: ubuntu
# By default Ray creates a new private keypair, but you can also use your own.
# If you do so, make sure to also set "KeyName" in the head and worker node
# configurations below.
# ssh_private_key: /path/to/your/key.pem
# Provider-specific config for the head node, e.g. instance type. By default
# Ray will auto-configure unspecified fields such as SubnetId and KeyName.
# For more documentation on available fields, see:
# http://boto3.readthedocs.io/en/latest/reference/services/ec2.html#EC2.ServiceResource.create_instances
head_node:
InstanceType: m4.16xlarge
ImageId: ami-06d51e91cea0dac8d # Ubuntu 18.04
# Set primary volume to 25 GiB
BlockDeviceMappings:
- DeviceName: /dev/sda1
Ebs:
VolumeSize: 100
# Additional options in the boto docs.
# Provider-specific config for worker nodes, e.g. instance type. By default
# Ray will auto-configure unspecified fields such as SubnetId and KeyName.
# For more documentation on available fields, see:
# http://boto3.readthedocs.io/en/latest/reference/services/ec2.html#EC2.ServiceResource.create_instances
worker_nodes:
InstanceType: m4.large
ImageId: ami-06d51e91cea0dac8d # Ubuntu 18.04
# Set primary volume to 25 GiB
BlockDeviceMappings:
- DeviceName: /dev/sda1
Ebs:
VolumeSize: 100
# Run workers on spot by default. Comment this out to use on-demand.
InstanceMarketOptions:
MarketType: spot
# Additional options can be found in the boto docs, e.g.
# SpotOptions:
# MaxPrice: MAX_HOURLY_PRICE
# Additional options in the boto docs.
# Files or directories to copy to the head and worker nodes. The format is a
# dictionary from REMOTE_PATH: LOCAL_PATH, e.g.
file_mounts: {
# "/path1/on/remote/machine": "/path1/on/local/machine",
# "/path2/on/remote/machine": "/path2/on/local/machine",
}
# List of shell commands to run to set up nodes.
setup_commands: []
# Uncomment these if you want to build ray from source.
# - sudo apt-get -qq update
# - sudo apt-get install -y build-essential curl unzip
# Install Anaconda.
- wget --quiet https://repo.continuum.io/archive/Anaconda3-5.0.1-Linux-x86_64.sh || true
- bash Anaconda3-5.0.1-Linux-x86_64.sh -b -p $HOME/anaconda3 || true
- echo 'export PATH="$HOME/anaconda3/bin:$PATH"' >> ~/.bashrc
# # Build Ray.
# - git clone https://github.com/ray-project/ray || true
# - ray/ci/travis/install-bazel.sh
- pip install -U pip
- conda uninstall -y terminado || true
- pip install terminado
- pip install boto3==1.4.8 cython==0.29.0
# - cd ray/python; git checkout master; git pull; pip install -e . --verbose
- "pip install https://s3-us-west-2.amazonaws.com/ray-wheels/{{ray_branch}}/{{commit}}/ray-{{ray_version}}-cp36-cp36m-manylinux1_x86_64.whl"
# Custom commands that will be run on the head node after common setup.
head_setup_commands: []
# Custom commands that will be run on worker nodes after common setup.
worker_setup_commands: []
# Command to start ray on the head node. You don't need to change this.
head_start_ray_commands:
- ray stop
- ulimit -n 65536; ray start --head --port=6379 --autoscaling-config=~/ray_bootstrap_config.yaml
# Command to start ray on worker nodes. You don't need to change this.
worker_start_ray_commands:
- ray stop
- ulimit -n 65536; ray start --address=$RAY_HEAD_IP:6379 --num-gpus=100
+1
View File
@@ -0,0 +1 @@
ray[debug]
+57
View File
@@ -0,0 +1,57 @@
#!/usr/bin/env bash
ray_version=""
commit=""
ray_branch=""
workload=""
for i in "$@"
do
echo "$i"
case "$i" in
--ray-version=*)
ray_version="${i#*=}"
;;
--commit=*)
commit="${i#*=}"
;;
--ray-branch=*)
ray_branch="${i#*=}"
;;
--workload=*)
workload="${i#*=}"
;;
--help)
usage
exit
;;
*)
echo "unknown arg, $i"
exit 1
;;
esac
done
echo "version: $ray_version"
echo "commit: $commit"
echo "branch: $ray_branch"
echo "workload: $workload"
wheel="https://s3-us-west-2.amazonaws.com/ray-wheels/$ray_branch/$commit/ray-$ray_version-cp36-cp36m-manylinux1_x86_64.whl"
# Install Anaconda.
wget --quiet https://repo.continuum.io/archive/Anaconda3-5.0.1-Linux-x86_64.sh || true
bash Anaconda3-5.0.1-Linux-x86_64.sh -b -p "$HOME/anaconda3" || true
# shellcheck disable=SC2016
echo 'export PATH="$HOME/anaconda3/bin:$PATH"' >> ~/.bashrc
conda uninstall -y terminado
source activate tensorflow_p36 && pip install -U pip
source activate tensorflow_p36 && pip install -U "$wheel"
pip install -U pip
conda uninstall -y terminado || true
pip install terminado
pip install boto3==1.4.8 cython==0.29.0
python "workloads/$workload.py"
@@ -0,0 +1,98 @@
#!/usr/bin/env python
import logging
import numpy as np
import sys
import time
import ray
logging.basicConfig(level=logging.INFO)
logger = logging.getLogger(__name__)
ray.init(address="auto")
# These numbers need to correspond with the autoscaler config file.
# The number of remote nodes in the autoscaler should upper bound
# these because sometimes nodes fail to update.
num_remote_nodes = 100
head_node_cpus = 2
num_remote_cpus = num_remote_nodes * head_node_cpus
# Wait until the expected number of nodes have joined the cluster.
while True:
num_nodes = len(ray.nodes())
logger.info("Waiting for nodes {}/{}".format(num_nodes,
num_remote_nodes + 1))
if num_nodes >= num_remote_nodes + 1:
break
time.sleep(5)
logger.info("Nodes have all joined. There are %s resources.",
ray.cluster_resources())
@ray.remote
class Child(object):
def __init__(self, death_probability):
self.death_probability = death_probability
def ping(self):
# Exit process with some probability.
exit_chance = np.random.rand()
if exit_chance > self.death_probability:
sys.exit(-1)
@ray.remote
class Parent(object):
def __init__(self, num_children, death_probability):
self.death_probability = death_probability
self.children = [
Child.remote(death_probability) for _ in range(num_children)
]
def ping(self, num_pings):
children_outputs = []
for _ in range(num_pings):
children_outputs += [
child.ping.remote() for child in self.children
]
try:
ray.get(children_outputs)
except Exception:
# Replace the children if one of them died.
self.__init__(len(self.children), self.death_probability)
def kill(self):
# Clean up children.
ray.get([child.__ray_terminate__.remote() for child in self.children])
num_parents = 10
num_children = 10
death_probability = 0.95
parents = [
Parent.remote(num_children, death_probability) for _ in range(num_parents)
]
start = time.time()
loop_times = []
for i in range(100):
loop_start = time.time()
ray.get([parent.ping.remote(10) for parent in parents])
# Kill a parent actor with some probability.
exit_chance = np.random.rand()
if exit_chance > death_probability:
parent_index = np.random.randint(len(parents))
parents[parent_index].kill.remote()
parents[parent_index] = Parent.remote(num_children, death_probability)
logger.info("Finished trial %s", i)
loop_times.append(time.time() - loop_start)
print("Finished in: {}s".format(time.time() - start))
print("Average iteration time: {}s".format(sum(loop_times) / len(loop_times)))
print("Max iteration time: {}s".format(max(loop_times)))
print("Min iteration time: {}s".format(min(loop_times)))
@@ -0,0 +1,188 @@
#!/usr/bin/env python
from collections import defaultdict
import numpy as np
import logging
import time
import ray
logging.basicConfig(level=logging.INFO)
logger = logging.getLogger(__name__)
ray.init(address="auto")
# These numbers need to correspond with the autoscaler config file.
# The number of remote nodes in the autoscaler should upper bound
# these because sometimes nodes fail to update.
num_remote_nodes = 100
head_node_cpus = 2
num_remote_cpus = num_remote_nodes * head_node_cpus
# Wait until the expected number of nodes have joined the cluster.
while True:
num_nodes = len(ray.nodes())
logger.info("Waiting for nodes {}/{}".format(num_nodes,
num_remote_nodes + 1))
if num_nodes >= num_remote_nodes + 1:
break
time.sleep(5)
logger.info("Nodes have all joined. There are %s resources.",
ray.cluster_resources())
# Require 1 GPU to force the tasks to be on remote machines.
@ray.remote(num_gpus=1)
def f(size, *xs):
return np.ones(size, dtype=np.uint8)
# Require 1 GPU to force the actors to be on remote machines.
@ray.remote(num_cpus=1, num_gpus=1)
class Actor(object):
def method(self, size, *xs):
return np.ones(size, dtype=np.uint8)
# Stage 0: Submit a bunch of small tasks with large returns.
stage_0_iterations = []
start_time = time.time()
logger.info("Submitting many tasks with large returns.")
for i in range(10):
iteration_start = time.time()
logger.info("Iteration %s", i)
ray.get([f.remote(1000000) for _ in range(1000)])
stage_0_iterations.append(time.time() - iteration_start)
stage_0_time = time.time() - start_time
logger.info("Finished stage 0 after %s seconds.", stage_0_time)
# Stage 1: Launch a bunch of tasks.
stage_1_iterations = []
start_time = time.time()
logger.info("Submitting many tasks.")
for i in range(10):
iteration_start = time.time()
logger.info("Iteration %s", i)
ray.get([f.remote(0) for _ in range(100000)])
stage_1_iterations.append(time.time() - iteration_start)
stage_1_time = time.time() - start_time
logger.info("Finished stage 1 after %s seconds.", stage_1_time)
# Launch a bunch of tasks, each with a bunch of dependencies. TODO(rkn): This
# test starts to fail if we increase the number of tasks in the inner loop from
# 500 to 1000. (approximately 615 seconds)
stage_2_iterations = []
start_time = time.time()
logger.info("Submitting tasks with many dependencies.")
x_ids = []
for _ in range(5):
iteration_start = time.time()
for i in range(20):
logger.info("Iteration %s. Cumulative time %s seconds", i,
time.time() - start_time)
x_ids = [f.remote(0, *x_ids) for _ in range(500)]
ray.get(x_ids)
stage_2_iterations.append(time.time() - iteration_start)
logger.info("Finished after %s seconds.", time.time() - start_time)
stage_2_time = time.time() - start_time
logger.info("Finished stage 2 after %s seconds.", stage_2_time)
# Create a bunch of actors.
start_time = time.time()
logger.info("Creating %s actors.", num_remote_cpus)
actors = [Actor.remote() for _ in range(num_remote_cpus)]
stage_3_creation_time = time.time() - start_time
logger.info("Finished stage 3 actor creation in %s seconds.",
stage_3_creation_time)
# Submit a bunch of small tasks to each actor. (approximately 1070 seconds)
start_time = time.time()
logger.info("Submitting many small actor tasks.")
for N in [1000, 100000]:
x_ids = []
for i in range(N):
x_ids = [a.method.remote(0) for a in actors]
if i % 100 == 0:
logger.info("Submitted {}".format(i * len(actors)))
ray.get(x_ids)
stage_3_time = time.time() - start_time
logger.info("Finished stage 3 in %s seconds.", stage_3_time)
# This tests https://github.com/ray-project/ray/issues/10150. The only way to
# integration test this is via performance. The goal is to fill up the cluster
# so that all tasks can be run, but spillback is required. Since the driver
# submits all these tasks it should easily be able to schedule each task in
# O(1) iterative spillback queries. If spillback behavior is incorrect, each
# task will require O(N) queries. Since we limit the number of inflight
# requests, we will run into head of line blocking and we should be able to
# measure this timing.
num_tasks = int(ray.cluster_resources()["GPU"])
logger.info(f"Scheduling many tasks for spillback.")
@ray.remote(num_gpus=1)
def func(t):
if t % 100 == 0:
logger.info(f"[spillback test] {t}/{num_tasks}")
start = time.perf_counter()
time.sleep(1)
end = time.perf_counter()
return start, end, ray.worker.global_worker.node.unique_id
results = ray.get([func.remote(i) for i in range(num_tasks)])
host_to_start_times = defaultdict(list)
for start, end, host in results:
host_to_start_times[host].append(start)
spreads = []
for host in host_to_start_times:
last = max(host_to_start_times[host])
first = min(host_to_start_times[host])
spread = last - first
spreads.append(spread)
logger.info(f"Spread: {last - first}\tLast: {last}\tFirst: {first}")
# avg_spread ~ 115 with Ray 1.0 scheduler. ~695 with (buggy) 0.8.7 scheduler.
avg_spread = sum(spreads) / len(spreads)
logger.info(f"Avg spread: {sum(spreads)/len(spreads)}")
print("Stage 0 results:")
print("\tTotal time: {}".format(stage_0_time))
print("Stage 1 results:")
print("\tTotal time: {}".format(stage_1_time))
print("\tAverage iteration time: {}".format(
sum(stage_1_iterations) / len(stage_1_iterations)))
print("\tMax iteration time: {}".format(max(stage_1_iterations)))
print("\tMin iteration time: {}".format(min(stage_1_iterations)))
print("Stage 2 results:")
print("\tTotal time: {}".format(stage_2_time))
print("\tAverage iteration time: {}".format(
sum(stage_2_iterations) / len(stage_2_iterations)))
print("\tMax iteration time: {}".format(max(stage_2_iterations)))
print("\tMin iteration time: {}".format(min(stage_2_iterations)))
print("Stage 3 results:")
print("\tActor creation time: {}".format(stage_3_creation_time))
print("\tTotal time: {}".format(stage_3_time))
print("Stage 4 results:")
print(f"\tScheduling spread: {avg_spread}.")
# TODO(rkn): The test below is commented out because it currently does not
# pass.
# # Submit a bunch of actor tasks with all-to-all communication.
# start_time = time.time()
# logger.info("Submitting actor tasks with all-to-all communication.")
# x_ids = []
# for _ in range(50):
# for size_exponent in [0, 1, 2, 3, 4, 5, 6]:
# x_ids = [a.method.remote(10**size_exponent, *x_ids) for a in actors]
# ray.get(x_ids)
# logger.info("Finished after %s seconds.", time.time() - start_time)
@@ -0,0 +1,155 @@
# This is stress test to run placement group.
# Please don't run it in the cluster
# setup yet. This test uses the cluster util to simulate the
# cluster environment.
import time
from time import perf_counter
from random import random
import ray
from ray.cluster_utils import Cluster
from ray.util.placement_group import (placement_group, remove_placement_group)
# TODO(sang): Increase the number in the actual stress test.
# This number should be divisible by 3.
resource_quantity = 666
num_nodes = 5
custom_resources = {"pg_custom": resource_quantity}
# Create pg that uses 1 resource of cpu & custom resource.
num_pg = resource_quantity
# TODO(sang): Cluster setup. Remove when running in real clusters.
cluster = Cluster()
nodes = []
for _ in range(num_nodes):
nodes.append(
cluster.add_node(
num_cpus=3, num_gpus=resource_quantity,
resources=custom_resources))
cluster.wait_for_nodes()
ray.init(address=cluster.address)
while not ray.is_initialized():
time.sleep(0.1)
# Scenario 1: Create bunch of placement groups and measure how long it takes.
total_creating_time = 0
total_removing_time = 0
repeat = 1
total_trial = repeat * num_pg
bundles = [{"GPU": 1, "pg_custom": 1}] * num_nodes
# Create and remove placement groups.
for _ in range(repeat):
pgs = []
for i in range(num_pg):
start = perf_counter()
pgs.append(placement_group(bundles, strategy="PACK", name=str(i)))
end = perf_counter()
total_creating_time += (end - start)
ray.get([pg.ready() for pg in pgs])
for pg in pgs:
start = perf_counter()
remove_placement_group(pg)
end = perf_counter()
total_removing_time += (end - start)
# Validate the correctness.
assert ray.cluster_resources()["GPU"] == num_nodes * resource_quantity
assert ray.cluster_resources()["pg_custom"] == num_nodes * resource_quantity
# Scenario 2:
# - Launch 30% of placement group in the driver and pass them.
# - Launch 70% of placement group at each remote tasks.
# - Randomly remove placement groups and schedule tasks and actors.
#
# Goal:
# - Make sure jobs are done without breaking GCS server.
# - Make sure all the resources are recovered after the job is done.
# - Measure the creation latency in the stressful environment.
@ray.remote(num_cpus=0, num_gpus=1, max_calls=0)
def mock_task():
time.sleep(0.1)
return True
@ray.remote(num_cpus=0, num_gpus=1, max_restarts=0)
class MockActor:
def __init__(self):
pass
def ping(self):
pass
@ray.remote(num_cpus=0)
def pg_launcher(pre_created_pgs, num_pgs_to_create):
pgs = []
pgs += pre_created_pgs
for i in range(num_pgs_to_create):
pgs.append(
placement_group(bundles, strategy="STRICT_SPREAD", name=str(i)))
pgs_removed = []
pgs_unremoved = []
# Randomly choose placement groups to remove.
for pg in pgs:
if random() < .5:
pgs_removed.append(pg)
else:
pgs_unremoved.append(pg)
tasks = []
max_actor_cnt = 5
actor_cnt = 0
actors = []
# Randomly schedule tasks or actors on placement groups that
# are not removed.
for pg in pgs_unremoved:
# TODO(sang): Comment in this line causes GCS actor management
# failure. We need to fix it.
if random() < .5:
tasks.append(mock_task.options(placement_group=pg).remote())
else:
if actor_cnt < max_actor_cnt:
actors.append(MockActor.options(placement_group=pg).remote())
actor_cnt += 1
# Remove the rest of placement groups.
for pg in pgs_removed:
remove_placement_group(pg)
ray.get([pg.ready() for pg in pgs_unremoved])
ray.get(tasks)
ray.get([actor.ping.remote() for actor in actors])
# Since placement groups are scheduled, remove them.
for pg in pgs_unremoved:
remove_placement_group(pg)
pre_created_num_pgs = round(num_pg * 0.3)
num_pgs_to_create = num_pg - pre_created_num_pgs
pg_launchers = []
for i in range(3):
pre_created_pgs = [
placement_group(bundles, strategy="STRICT_SPREAD")
for _ in range(pre_created_num_pgs // 3)
]
pg_launchers.append(
pg_launcher.remote(pre_created_pgs, num_pgs_to_create // 3))
ray.get(pg_launchers)
assert ray.cluster_resources()["GPU"] == num_nodes * resource_quantity
assert ray.cluster_resources()["pg_custom"] == num_nodes * resource_quantity
ray.shutdown()
print("Avg placement group creating time: "
f"{total_creating_time / total_trial * 1000} ms")
print("Avg placement group removing time: "
f"{total_removing_time / total_trial* 1000} ms")
print("Stress Test succeed.")