Introduction and Basic Execution
In this tutorial, you will learn the fundamentals of Argobots by creating and executing your first user-level thread (ULT). This is the foundation for all Argobots programs and will introduce you to the basic lifecycle of an Argobots application.
Key Concepts
Before diving into the code, let’s understand the fundamental concepts:
- Initialization and Finalization
Every Argobots program begins with
ABT_init()and ends withABT_finalize(). Initialization sets up the Argobots runtime and creates a primary execution stream. Finalization cleans up all resources and ensures proper shutdown.- Execution Streams (xstreams)
An execution stream is an OS-level thread that executes Argobots work units. When you call
ABT_init(), Argobots automatically creates a primary execution stream that runs on the main thread. You can create additional execution streams for parallel execution (covered in Tutorial 02).- User-Level Threads (ULTs)
ULTs are lightweight threads managed entirely in user space by Argobots. Unlike OS threads (pthreads), ULTs have very low creation overhead and can be created in large numbers. Each ULT executes a function you provide.
- Pools
Pools are work queues that hold ULTs waiting to be executed. Each execution stream has at least one pool. When you create a ULT, you specify which pool it should be added to. The execution stream’s scheduler pulls ULTs from the pool and executes them.
Basic Example: Hello World
Let’s start with a simple example that creates a single ULT:
1/*
2 * Basic Argobots example: Creating and executing a simple ULT
3 */
4
5#include <stdio.h>
6#include <abt.h>
7
8/* Thread function that will be executed by the ULT */
9void hello_world_fn(void *arg)
10{
11 int thread_id = *((int *)arg);
12 printf("Hello from ULT %d!\n", thread_id);
13}
14
15int main(int argc, char **argv)
16{
17 ABT_xstream xstream; /* Execution stream handle */
18 ABT_pool pool; /* Pool handle */
19 ABT_thread thread; /* ULT handle */
20 int thread_arg = 1;
21
22 /* Step 1: Initialize Argobots */
23 ABT_init(argc, argv);
24 printf("Argobots initialized\n");
25
26 /* Step 2: Get the primary execution stream (created automatically by ABT_init) */
27 ABT_xstream_self(&xstream);
28 printf("Got primary execution stream\n");
29
30 /* Step 3: Get the pool associated with the primary execution stream */
31 ABT_xstream_get_main_pools(xstream, 1, &pool);
32 printf("Got main pool\n");
33
34 /* Step 4: Create a ULT and add it to the pool */
35 ABT_thread_create(pool, hello_world_fn, &thread_arg,
36 ABT_THREAD_ATTR_NULL, &thread);
37 printf("Created ULT\n");
38
39 /* Step 5: Wait for the ULT to complete */
40 ABT_thread_join(thread);
41 printf("ULT completed\n");
42
43 /* Step 6: Free the ULT */
44 ABT_thread_free(&thread);
45 printf("ULT freed\n");
46
47 /* Step 7: Finalize Argobots */
48 ABT_finalize();
49 printf("Argobots finalized\n");
50
51 return 0;
52}
Code Walkthrough
Let’s examine each step of the program:
- Step 1: Initialization
ABT_init(argc, argv);
This initializes the Argobots runtime system. It:
Sets up internal data structures
Creates a primary execution stream
Creates a default pool for the primary execution stream
Converts the main thread into an Argobots execution stream
- Step 2: Get the Primary Execution Stream
ABT_xstream_self(&xstream);
This retrieves a handle to the current execution stream (in this case, the primary execution stream created by
ABT_init()). We need this handle to access the execution stream’s pool.- Step 3: Get the Main Pool
ABT_xstream_get_main_pools(xstream, 1, &pool);
This retrieves the default pool associated with the execution stream. The second parameter (1) specifies we want to retrieve one pool. Each execution stream can have multiple pools; we’re getting the main pool here.
- Step 4: Create a ULT
ABT_thread_create(pool, hello_world_fn, &thread_arg, ABT_THREAD_ATTR_NULL, &thread);
This creates a ULT with the following parameters:
pool: The pool where the ULT will be addedhello_world_fn: The function to execute&thread_arg: Argument passed to the functionABT_THREAD_ATTR_NULL: Use default attributes (stack size, etc.)&thread: Output handle for the created ULT
Note that this is a non-blocking operation. The ULT is created and added to the pool, but it may not execute immediately.
- Step 5: Wait for Completion
ABT_thread_join(thread);
This is a blocking operation that will not return until the ULT has finished executing.
- Step 6: Free the ULT
ABT_thread_free(&thread);
Frees the resources associated with the ULT.
- Step 6: Finalization
ABT_finalize();
This shuts down the Argobots runtime:
Waits for all work units to complete
Destroys all execution streams, pools, and schedulers
Frees all internal resources
After calling
ABT_finalize(), you cannot use any Argobots functions.
Expected Output
When you run the program, you should see output similar to:
Argobots initialized
Got primary execution stream
Got main pool
Created ULT
Hello from ULT 1!
ULT completed
ULT freed
Argobots finalized
The exact order of messages may vary slightly depending on when the ULT executes, but the “Hello from ULT 1!” message will always appear before “ULT completed”.
Important Notes
- Non-Blocking Creation
Creating a ULT with
ABT_thread_create()does not block. The ULT is added to the pool and will be executed when the scheduler decides. In this simple example with a single execution stream, the scheduler will execute the ULT when we callABT_thread_join().- Implicit Progress
Certain Argobots operations (like
ABT_thread_join()) trigger implicit progress of the scheduler. This means the scheduler may execute pending work units even though you didn’t explicitly call a yield or scheduling function.- The Primary Execution Stream
The primary execution stream is special - it’s created automatically by
ABT_init()and represents the main thread of your program. You cannot free or join the primary execution stream; it is automatically cleaned up byABT_finalize().
Note
Passing NULL instead of &thread as the 5th argument to
ABT_thread_create will create an anonymous ULT. Such a ULT will
automatically be freed once it has finished executing.
API Reference
This tutorial covered the following Argobots functions:
- Initialization and Finalization
int ABT_init(int argc, char **argv)Initialize the Argobots runtime. Must be called before any other Argobots function.
int ABT_finalize(void)Finalize the Argobots runtime. Must be called to properly shut down Argobots.
- Execution Stream Functions
int ABT_xstream_self(ABT_xstream *xstream)Get a handle to the current execution stream.
int ABT_xstream_get_main_pools(ABT_xstream xstream, int max_pools, ABT_pool *pools)Retrieve the main pools associated with an execution stream.
- ULT Functions
int ABT_thread_create(ABT_pool pool, void (*thread_func)(void *), void *arg, ABT_thread_attr attr, ABT_thread *newthread)Create a new ULT and add it to the specified pool.
int ABT_thread_join(ABT_thread thread)Join (wait for completion of) the ULT. This is a blocking operation.
int ABT_thread_free(ABT_thread *thread)Free a ULT.
- Special Constants
ABT_THREAD_ATTR_NULLUse default attributes when creating a ULT (stack size, etc.)