From b3a3775586744f9e5e086d8ad5bd8297d24f9e0d Mon Sep 17 00:00:00 2001 From: Charles Frye Date: Wed, 14 Feb 2024 15:48:03 -0500 Subject: [PATCH] Updates to `hello_world` example (#583) * updates hello world example * incorporating feedback from @ekzhang --- 01_getting_started/hello_world.py | 113 ++++++++++++++++++------------ 1 file changed, 67 insertions(+), 46 deletions(-) diff --git a/01_getting_started/hello_world.py b/01_getting_started/hello_world.py index a3fe60329..522d735ef 100644 --- a/01_getting_started/hello_world.py +++ b/01_getting_started/hello_world.py @@ -1,16 +1,15 @@ # # Hello, world! # -# This is a trivial example of a Modal function, but it illustrates a few features: +# This tutorial demonstrates some core features of Modal: # -# * You can print things to stdout and stderr. -# * You can return data. -# * You can map over a function. +# * You can run functions on Modal just as easily as you run them locally. +# * Running functions in parallel on Modal is simple and fast. +# * Logs and errors show up immediately, even for functions running on Modal. # -# ## Import Modal and define the app +# ## Importing Modal and setting up # -# Let's start with the top level imports. -# You need to import Modal and define the app. -# A stub is an object that defines everything that will be run. +# We start by importing `modal` and creating a `Stub`. +# We build up from our `Stub` to [define our application](/docs/guide/apps). import sys @@ -20,10 +19,18 @@ # ## Defining a function # -# Here we define a Modal function using the `modal.function` decorator. -# The body of the function will automatically be run remotely. -# This particular function is pretty silly: it just prints "hello" -# and "world" alternatingly to standard out and standard error. +# Modal takes code and runs it in the cloud. +# +# So first we've got to write some code. +# +# Let's write a simple function: +# log `"hello"` to standard out if the input is even +# or `"world"` to standard error if it's not, +# then return the input times itself. +# +# To make this function work with Modal, we just wrap it in a decorator +# from our application `stub`, +# [`@stub.function`](docs/reference/modal.Stub#function). @stub.function() @@ -36,32 +43,28 @@ def f(i): return i * i -# ## Running it +# ## Running our function locally, remotely, and in parallel # -# Finally, let's actually invoke it. -# We put this invocation code inside a `@stub.local_entrypoint()`. -# This is because this module will be imported in the cloud, and we don't want -# this code to be executed a second time in the cloud. +# Now let's see three different ways we can call that function: # -# Run `modal run hello_world.py` and the `@stub.local_entrypoint()` decorator will handle -# starting the Modal app and then executing the wrapped function body. +# 1. As a regular `local` call on your computer, with `f.local` # -# Inside the `main()` function body, we are calling the function `f` in three ways: +# 2. As a `remote` call that runs in the cloud, with `f.remote` # -# 1 As a simple local call, `f.local(1000)` -# 2. As a simple *remote* call `f.remote(1000)` -# 3. By mapping over the integers `0..19` +# 3. By `map`ping many copies of `f` in the cloud over many inputs, with `f.map` +# +# We call `f` in each of these ways inside a `main` function below. @stub.local_entrypoint() def main(): - # Call the function locally. + # run the function locally print(f.local(1000)) - # Call the function remotely. + # run the function remotely on Modal print(f.remote(1000)) - # Parallel map. + # run the function in parallel and remotely on Modal total = 0 for ret in f.map(range(20)): total += ret @@ -69,35 +72,53 @@ def main(): print(total) -# ## What happens? +# Enter `modal run hello_world.py` in a shell and you'll see +# a Modal app initialize. +# You'll then see the `print`ed logs of +# the `main` function and, mixed in with them, all the logs of `f` as it is run +# locally, then remotely, and then remotely and in parallel. +# +# That's all triggered by adding the [`@stub.local_entrypoint`](docs/reference/modal.Stub#local_entrypoint) decorator on `main`, +# which defines it as the function to start from locally when we invoke `modal run`. # -# When you do `.remote` on function `f`, Modal will execute `f` **in the cloud,** -# not locally on your computer. It will take the code, put it inside a -# container, run it, and stream all the output back to your local -# computer. +# ## What just happened? # -# Try doing one of these things next. +# When we called `.remote` on `f`, the function was executed +# **in the cloud**, on Modal's infrastructure, not locally on our computer. # -# ### Change the code and run again +# In short, we took the function `f`, put it inside a container, +# sent it the inputs, and streamed back the logs and outputs. # -# For instance, change the `print` statement in the function `f`. -# You can see that the latest code is always run. +# ## But why does this matter? +# +# Try doing one of these things next to start seeing the full power of Modal! +# +# ### You can change the code and run it again +# +# For instance, change the `print` statement in the function `f` +# to print `"spam"` and `"eggs"` instead and run the app again. +# You'll see that that your new code is run with no extra work from you -- +# and it should even run faster! # # Modal's goal is to make running code in the cloud feel like you're -# running code locally. You don't need to run any commands to rebuild, -# push containers, or go to a web UI to download logs. +# running code locally. That means no waiting for long image builds when you've just moved a comma, +# no fiddling with container image pushes, and no context-switching to a web UI to inspect logs. +# +# ### You can map over more data +# +# Change the `map` range from `20` to some large number, like `1170`. You'll see +# Modal create and run even more containers in parallel this time. # -# ### Map over a larger dataset +# And it'll happen lightning fast! # -# Change the map range from 20 to some large number. You can see that -# Modal will create and run more containers in parallel. +# ### You can run a more interesting function # -# The function `f` is obviously silly and doesn't do much, but you could -# imagine something more significant, like: +# The function `f` is obviously silly and doesn't do much, but in its place +# imagine something that matters to you, like: # -# * Training a machine learning model -# * Transcoding media -# * Backtesting a trading algorithm. +# * Running [language model inference](/docs/examples/vllm_mixtral) or [fine-tuning](/docs/examples/slack-finetune) +# * Manipulating [audio](/docs/examples/discord-musicgen) or [images](stable_diffusion_xl_turbo) +# * [Collecting financial data](/docs/examples/fetch_stock_prices) to backtest a trading algorithm. # -# Modal lets you parallelize that operation trivially by running hundreds or +# Modal lets you parallelize that operation effortlessly by running hundreds or # thousands of containers in the cloud.