unplugged-system/test/dittosuite/README.md

451 lines
14 KiB
Markdown

# Dittosuite
Dittosuite is a work in progress collection of tools that aims at providing
a high-level language called Dittolang that defines operations.
The defined Dittolang operations can be interpreted by Dittosim for simulation
to provide a simulated performance measurement and quickly identify
the goodness of a solution.
Specularly, Dittobench interprets the Dittolang operations and executes them on
a real device, tracking the behavior and measuring the performance.
# How to run
```
$ ./dittobench [options] [.ditto file]
```
To run a benchmark, a well formed .ditto file must be provided, see section
[How to write .ditto files](#how-to-write-ditto-files)
In addition, these options can be set:
- `--results-output=<int | string>` (default: report). Select the results output format.
Options: report, csv with 0, 1 respectively.
- `--log-stream=<int | string>` (default: stdout). Select the output stream for the log messages.
Options: stdout, logcat with 0, 1 respectively.
- `--log-level=<int | string>` (default: INFO). Select to output messages which are at or below
the set level. Options: VERBOSE, DEBUG, INFO, WARNING, ERROR, FATAL with 0, 1, 2, 3, 4 and 5
respectively.
- `--parameters=string`. If the benchmark is parametric, all the parameters (separated by commas)
can be given through this option.
# How to write .ditto files
Every .ditto file should begin with this skeleton:
```
main: {
...
},
global {
...
}
```
Optionally, it can contain `init` and `clean_up` sections:
```
init: {
...
},
main: {
...
},
clean_up: {
...
},
global {
...
}
```
## `global`
Global section should contain general benchmark configuration. Currently available options:
- (optional) `string absolute_path` (`default = ""`). Specifies the absolute path for the files.
## `init`
`init` is optional and can be used to initialize the benchmarking environment. It executes
instructions similar to `main`, but the results are not collected in the end.
## `main`
`main` is the entry point for the benchmark. It can contain a single `instruction` or
`instruction_set` (also with nested `instruction_set`).
## `clean_up`
`clean_up` is optional and can be used to reset the benchmarking environment to the initial state,
e.g, delete benchmark files. Similar to `init`, it executes instructions like `main`, but results
are not collected in the end.
## `instruction`
```
{
<name of the instruction>: {
<first argument>,
<second argument>,
...
},
<general instruction options>
}
```
Currently available options:
- (optional) `int repeat` (`default = 1`). Specifies how many times the instruction should be
repeated.
## `instruction_set`
```
{
instruction_set: {
instructions: {
{
<name of the first instruction>: {
<first argument>,
<second argument>,
...
},
<general instruction options>
},
{
<name of the second instruction>: {
<first argument>,
<second argument>,
...
},
<general instruction options>
},
...
},
iterate_options: {...}
},
<general instruction options>
}
```
Instruction set is an Instruction container that executes the contained instructions sequentially.
Instruction set can optionally iterate over a list and execute the provided set of instructions on
each item from the list. To use it, `iterate_options` should be set with these options:
- `string list_name` - Shared variable name pointing to a list of values.
- `string item_name` - Shared variable name to which a selected value should be stored.
- (optional) `Order order` (`default = SEQUENTIAL`) - Specifies if
the elements of the list should be accessed sequentially or randomly. Options:
`SEQUENTIAL`, `RANDOM`.
- (optional) `Reseeding reseeding` (`default = ONCE`) - Specifies how often the random number
generator should be reseeded with the same provided (or generated) seed. Options: `ONCE`,
`EACH_ROUND_OF_CYCLES`, `EACH_CYCLE`.
- (optional) `uint32 seed` - Seed for the random number generator. If the seed is not provided,
current system time is used as the seed.
## `multithreading` and `threads`
```
multithreading: {
threads: [
{
instruction: {...},
spawn: <number of threads to spawn with the provided instruction>
},
...
]
}
```
Multithreading is another instruction container that executes the specified instructions
(or instruction sets) in different threads. If the optional `spawn` option for a specific
instruction (or instruction set) is provided, then the provided number of threads will be created
for it.
## Example
```
main: {
instruction_set: {
instructions: [
{
open_file: {
path_name: "newfile2.txt",
output_fd: "test_file"
}
},
{
close_file: {
input_fd: "test_file"
}
}
]
},
repeat: 10
},
global {
absolute_path: "/data/local/tmp/";
}
```
See more examples in `example/`.
# Predefined list of instructions
## `open_file`
Opens the file with a file path or a shared variable name pointing to a file path. If neither of
those are provided, a random name consisting of 9 random digits is generated. Optionally saves the
file descriptor which can then be used by subsequent instructions. Also, can optionally create the
file if it does not already exist.
### Arguments:
- (optional) `string path_name` - Specifies the file path.<br/>
OR<br/>
`string input` - Shared variable name pointing to a file path.
- (optional) `string output_fd` - Shared variable name to which output file descriptor
should be saved.
- (optional) `bool create` (`default = true`) - Specifies if the file should be created if it does
not already exist. If the file exists, nothing happens.
## `delete_file`
Deletes the file with a file path or a shared variable name pointing to a file path.
Uses `unlink(2)`.
### Arguments:
- `string path_name` - Specifies the file path.<br/>
OR<br/>
`string input` - Shared variable name pointing to a file path.
## `close_file`
Closes the file with the provided file descriptor.
Uses `close(2)`.
### Arguments:
- `string input_fd` - Shared variable name pointing to a file descriptor.
## `resize_file`
Resizes the file with the provided file descriptor and new size. If the provided size is greater
than the current file size, `fallocate(2)` is used, while `ftruncate(2)` is used if the provided
size is not greater than the current file size.
### Arguments:
- `string input_fd` - Shared variable name pointing to a file descriptor.
- `int64 size` - New file size (in bytes).
## `resize_file_random`
Resizes the file with the provided file descriptor and a range for new size. New file size is
randomly generated in the provided range and if the generated size is greater
than the current file size, `fallocate(2)` is used, while `ftruncate(2)` is used if the generated
size is not greater than the current file size.
### Arguments:
- `string input_fd` - Shared variable name pointing to a file descriptor.
- `int64 min` - Minimum value (in bytes)
- `int64 max` - Maximum value (in bytes)
- (optional) `uint32 seed` - Seed for the random number generator. If the seed is not provided,
current system time is used as the seed.
- (optional) `Reseeding reseeding` (`default = ONCE`). How often the random number
generator should be reseeded with the provided (or generated) seed. Options: `ONCE`,
`EACH_ROUND_OF_CYCLES`, `EACH_CYCLE`.
## `write_file`
Writes to file with the provided file descriptor. For `SEQUENTIAL`
access, the blocks of data will be written sequentially and if the end of
the file is reached, new blocks will start from the beginning of the file. For
`RANDOM` access, the block offset, to which data should be written, will
be randomly chosen with uniform distribution. `10101010` byte is used for the
write operation to fill the memory with alternating ones and zeroes. Uses
`pwrite64(2)`.
### Arguments:
- `string input_fd` - Shared variable name pointing to a file descriptor.
- (optional) `int64 size` (`default = -1`) - How much data (in bytes) should be written in total.
If it is set to `-1`, then file size is used.
- (optional) `int64 block_size` (`default = 4096`) - How much data (in bytes) should be written at
once. If it is set to `-1`, then file size is used.
- (optional) `int64 starting_offset` (`default = 0`) - If `access_order` is
set to `SEQUENTIAL`, then the blocks, to which the data should be written,
will start from this starting offset (in bytes).
- (optional) `Order access_order` (`default = SEQUENTIAL`) - Order of the
write. Options: `SEQUENTIAL` and `RANDOM`.
- (optional) `uint32 seed` - Seed for the random number generator. If the seed is not provided,
current system time is used as the seed.
- (optional) `bool fsync` (`default = false`) - If set, `fsync(2)` will be called after the
execution of all write operations.
- (optional) `Reseeding reseeding` (`default = ONCE`) - How often the random number
generator should be reseeded with the provided (or generated) seed. Options: `ONCE`,
`EACH_ROUND_OF_CYCLES`, `EACH_CYCLE`.
## `read_file`
Reads from file with the provided file descriptor. For `SEQUENTIAL`
access, the blocks of data will be read sequentially and if the end of
the file is reached, new blocks will start from the beginning of the file. For
`RANDOM` access, the block offset, from which data should be read, will
be randomly chosen with uniform distribution. Calls `posix_fadvise(2)` before
the read operations. Uses `pread64(2)`.
### Arguments:
- `string input_fd` - Shared variable name pointing to a file descriptor.
- (optional) `int64 size` (`default = -1`) - How much data (in bytes) should be read in total.
If it is set to `-1`, then file size is used.
- (optional) `int64 block_size` (`default = 4096`) - How much data (in bytes) should be read at
once. If it is set to `-1`, then file size is used.
- (optional) `int64 starting_offset` (`default = 0`) - If `access_order` is
set to `SEQUENTIAL`, then the blocks, from which the data should be read,
will start from this starting offset (in bytes).
- (optional) `Order access_order` (`default = SEQUENTIAL`) - Order of the
read. Options: `SEQUENTIAL` and `RANDOM`.
- (optional) `uint32 seed` - Seed for the random number generator. If the seed is not provided,
current system time is used as the seed.
- (optional) `ReadFAdvise fadvise` (`default = AUTOMATIC`) - Sets the argument
for the `posix_fadvise(2)` operation. Options: `AUTOMATIC`, `NORMAL`,
`SEQUENTIAL` and `RANDOM`. If `AUTOMATIC` is set, then
`POSIX_FADV_SEQUENTIAL` or `POSIX_FADV_RANDOM` will be used for `SEQUENTIAL`
and `RANDOM` access order respectively.
- (optional) `Reseeding reseeding` (`default = ONCE`) - How often the random number
generator should be reseeded with the provided (or generated) seed. Options: `ONCE`,
`EACH_ROUND_OF_CYCLES`, `EACH_CYCLE`.
## `read_directory`
Reads file names from a directory and stores them as a list in a shared variable. Uses `readdir(3)`.
### Arguments:
- `string directory_name` - Name of the directory
- `string output` - Shared variable name to which files names should be saved.
## `invalidate_cache`
Drops kernel clean caches, including, dentry, inode and page caches by calling sync() first and
then writing `3` to `/proc/sys/vm/drop_caches`. No arguments.
# Dependencies
## Android
The project is currently being developed as part of the Android Open Source
Project (AOSP) and is supposed to run out-of-the-box.
## Linux
The following utilities are required to build the project on Linux:
```
sudo apt install cmake protobuf-compiler
```
# Testing
## Linux
A suite of unit tests is provided in the test/ directory.
In Linux these tests can be run with the following commands:
```
mkdir build
cd build
make
cd test
ctest
```
# Use cases
## File operations performance (high priority)
Bandwidth and measurement when dealing with few huge files or many small files.
Operations are combinations of sequential/random-offset read/write.
Latency in creating/deleting files/folders.
These operations should be able to be triggered in a multiprogrammed fashion.
## Display pipeline
A graph of processes that are communicating with each others in a pipeline of
operations that are parallely contributing to the generation of display frames.
## Scheduling (low priority)
Spawning tasks (period, duration, deadline) and verifying their scheduling
latency and deadline misses count.
# Workflow example and implementation nits
In the following scenario, two threads are running.
T1 runs the following operations: Read, Write, Read, sends a request to T2 and
waits for the reply, then Write, Read.
T2 waits for a request, then Read, Write, then sends the reply to the requester.
Operations are encoded as primitives expressed with ProtoBuffers.
The definition of dependencies among threads can be represented as graphs.
## Initialization phase
The first graph traversal is performed at initialization time, when all the
ProtoBuffer configuration files are distributed among all the binaries so that
they can perform all the heavy initialization duties.
## Execution phase
After the initialization phase completes the graph can be traversed again to
put all the workloads in execution.
## Results gathering phase
A final graph traversal can be performed to fetch all the measurements that
each entity internally stored.
## Post processing
All the measurements must be ordered and later processed to provide useful
information to the user.
T1: INIT : [ RD WR RD ] SND RCV [ WR RD ] : END
T2: INIT : RCV [ RD WR ] SND : END
# Scratch notes
critical path [ READ WRITE READ ] [ READ WRITE ] [ WRITE READ ]
-------------------->
> <
Thread1 III-XXXXXX|X-SSSSSS-XX-TTTT
Thread2 III-XXXX|XXX-TTTT
^
> XXXXXXX XX<
XXXX
READ WRITE READ
--->
vector<instr*> {read(), write(), read()};
-> start()
RECEIVE READ WRITE READ SEND
--->
vector<instr*> {receive(), read(), write(), read(), send()};
start()
lock on receive()