Skip to content

Commit

Permalink
Large memory usage test cases and benchmarks (#619)
Browse files Browse the repository at this point in the history
* Large memory usage test cases and benchmarks

* Ignore
  • Loading branch information
vigoo authored Jun 26, 2024
1 parent ced89ab commit 9904f42
Show file tree
Hide file tree
Showing 18 changed files with 471 additions and 1 deletion.
48 changes: 48 additions & 0 deletions golem-worker-executor-base/tests/guest_languages.rs
Original file line number Diff line number Diff line change
Expand Up @@ -539,3 +539,51 @@ async fn c_example_2() {

check!(first_line == format!("Hello C! {year}"));
}

#[tokio::test]
#[tracing::instrument]
#[ignore]
async fn c_example_3() {
let context = TestContext::new();
let executor = start(&context).await.unwrap();

let component_id = executor.store_component("large-initial-memory").await;
let worker_id = executor
.start_worker(&component_id, "large-initial-memory")
.await;

executor.log_output(&worker_id).await;

let result = executor
.invoke_and_await(&worker_id, "run", vec![])
.await
.unwrap();

drop(executor);

check!(result == vec![Value::U64(536870912)]);
}

#[tokio::test]
#[tracing::instrument]
#[ignore]
async fn c_example_4() {
let context = TestContext::new();
let executor = start(&context).await.unwrap();

let component_id = executor.store_component("large-dynamic-memory").await;
let worker_id = executor
.start_worker(&component_id, "large-dynamic-memory")
.await;

executor.log_output(&worker_id).await;

let result = executor
.invoke_and_await(&worker_id, "run", vec![])
.await
.unwrap();

drop(executor);

check!(result == vec![Value::U64(0)]);
}
8 changes: 8 additions & 0 deletions integration-tests/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,14 @@ path = "src/benchmarks/throughput.rs"
name = "benchmark_rpc"
path = "src/benchmarks/rpc.rs"

[[bin]]
name = "benchmark_large_initial_memory"
path = "src/benchmarks/large_initial_memory.rs"

[[bin]]
name = "benchmark_large_dynamic_memory"
path = "src/benchmarks/large_dynamic_memory.rs"

[[bin]]
name = "benchmark_report"
path = "src/benchmarks/report/benchmark_report.rs"
128 changes: 128 additions & 0 deletions integration-tests/src/benchmarks/large_dynamic_memory.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,128 @@
// Copyright 2024 Golem Cloud
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

use std::time::SystemTime;

use async_trait::async_trait;

use golem_test_framework::config::{CliParams, TestDependencies};
use golem_test_framework::dsl::benchmark::{Benchmark, BenchmarkRecorder, RunConfig};
use golem_test_framework::dsl::TestDsl;
use integration_tests::benchmarks::{
cleanup_iteration, run_benchmark, setup_benchmark, setup_iteration, BenchmarkContext,
IterationContext,
};

struct LargeDynamicMemory {
config: RunConfig,
}

#[async_trait]
impl Benchmark for LargeDynamicMemory {
type BenchmarkContext = BenchmarkContext;
type IterationContext = IterationContext;

fn name() -> &'static str {
"large-dynamic-memory"
}

async fn create_benchmark_context(
params: CliParams,
cluster_size: usize,
) -> Self::BenchmarkContext {
setup_benchmark(params, cluster_size).await
}

async fn cleanup(benchmark_context: Self::BenchmarkContext) {
benchmark_context.deps.kill_all()
}

async fn create(_params: CliParams, config: RunConfig) -> Self {
Self { config }
}

async fn setup_iteration(
&self,
benchmark_context: &Self::BenchmarkContext,
) -> Self::IterationContext {
setup_iteration(
benchmark_context,
self.config.clone(),
"large-initial-memory",
false,
)
.await
}

async fn warmup(
&self,
benchmark_context: &Self::BenchmarkContext,
context: &Self::IterationContext,
) {
if let Some(worker_id) = context.worker_ids.first() {
let start = SystemTime::now();
benchmark_context
.deps
.invoke_and_await(worker_id, "run", vec![])
.await
.expect("invoke_and_await failed");
let elapsed = start.elapsed().expect("SystemTime elapsed failed");
println!("Warmup invocation took {:?}", elapsed);
}
}

async fn run(
&self,
benchmark_context: &Self::BenchmarkContext,
context: &Self::IterationContext,
recorder: BenchmarkRecorder,
) {
// Start each worker and invoke `run` - each worker takes an initial 512Mb memory
let mut fibers = Vec::new();
for (n, worker_id) in context.worker_ids.iter().enumerate() {
let context_clone = benchmark_context.clone();
let worker_id_clone = worker_id.clone();
let recorder_clone = recorder.clone();
let fiber = tokio::task::spawn(async move {
let start = SystemTime::now();
context_clone
.deps
.invoke_and_await(&worker_id_clone, "run", vec![])
.await
.expect("invoke_and_await failed");
let elapsed = start.elapsed().expect("SystemTime elapsed failed");
recorder_clone.duration(&"invocation".to_string(), elapsed);
recorder_clone.duration(&format!("worker-{n}"), elapsed);
});
fibers.push(fiber);
}

for fiber in fibers {
fiber.await.expect("fiber failed");
}
}

async fn cleanup_iteration(
&self,
benchmark_context: &Self::BenchmarkContext,
context: Self::IterationContext,
) {
cleanup_iteration(benchmark_context, context).await
}
}

#[tokio::main]
async fn main() {
run_benchmark::<LargeDynamicMemory>().await;
}
128 changes: 128 additions & 0 deletions integration-tests/src/benchmarks/large_initial_memory.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,128 @@
// Copyright 2024 Golem Cloud
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

use std::time::SystemTime;

use async_trait::async_trait;

use golem_test_framework::config::{CliParams, TestDependencies};
use golem_test_framework::dsl::benchmark::{Benchmark, BenchmarkRecorder, RunConfig};
use golem_test_framework::dsl::TestDsl;
use integration_tests::benchmarks::{
cleanup_iteration, run_benchmark, setup_benchmark, setup_iteration, BenchmarkContext,
IterationContext,
};

struct LargeInitialMemory {
config: RunConfig,
}

#[async_trait]
impl Benchmark for LargeInitialMemory {
type BenchmarkContext = BenchmarkContext;
type IterationContext = IterationContext;

fn name() -> &'static str {
"large-initial-memory"
}

async fn create_benchmark_context(
params: CliParams,
cluster_size: usize,
) -> Self::BenchmarkContext {
setup_benchmark(params, cluster_size).await
}

async fn cleanup(benchmark_context: Self::BenchmarkContext) {
benchmark_context.deps.kill_all()
}

async fn create(_params: CliParams, config: RunConfig) -> Self {
Self { config }
}

async fn setup_iteration(
&self,
benchmark_context: &Self::BenchmarkContext,
) -> Self::IterationContext {
setup_iteration(
benchmark_context,
self.config.clone(),
"large-initial-memory",
false,
)
.await
}

async fn warmup(
&self,
benchmark_context: &Self::BenchmarkContext,
context: &Self::IterationContext,
) {
if let Some(worker_id) = context.worker_ids.first() {
let start = SystemTime::now();
benchmark_context
.deps
.invoke_and_await(worker_id, "run", vec![])
.await
.expect("invoke_and_await failed");
let elapsed = start.elapsed().expect("SystemTime elapsed failed");
println!("Warmup invocation took {:?}", elapsed);
}
}

async fn run(
&self,
benchmark_context: &Self::BenchmarkContext,
context: &Self::IterationContext,
recorder: BenchmarkRecorder,
) {
// Start each worker and invoke `run` - each worker takes an initial 512Mb memory
let mut fibers = Vec::new();
for (n, worker_id) in context.worker_ids.iter().enumerate() {
let context_clone = benchmark_context.clone();
let worker_id_clone = worker_id.clone();
let recorder_clone = recorder.clone();
let fiber = tokio::task::spawn(async move {
let start = SystemTime::now();
context_clone
.deps
.invoke_and_await(&worker_id_clone, "run", vec![])
.await
.expect("invoke_and_await failed");
let elapsed = start.elapsed().expect("SystemTime elapsed failed");
recorder_clone.duration(&"invocation".to_string(), elapsed);
recorder_clone.duration(&format!("worker-{n}"), elapsed);
});
fibers.push(fiber);
}

for fiber in fibers {
fiber.await.expect("fiber failed");
}
}

async fn cleanup_iteration(
&self,
benchmark_context: &Self::BenchmarkContext,
context: Self::IterationContext,
) {
cleanup_iteration(benchmark_context, context).await
}
}

#[tokio::main]
async fn main() {
run_benchmark::<LargeInitialMemory>().await;
}
2 changes: 1 addition & 1 deletion test-components/build-components.sh
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ js_test_components=("js-1" "js-2" "js-3" "js-4" "js-echo")
java_test_components=("java-1" "java-2")
dotnet_test_components=("csharp-1")
swift_test_components=("swift-1")
c_test_components=("c-1")
c_test_components=("c-1" "large-initial-memory" "large-dynamic-memory")
python_test_components=("python-1" "py-echo")

# Optional arguments:
Expand Down
Binary file modified test-components/c-1.wasm
Binary file not shown.
Binary file added test-components/large-dynamic-memory.wasm
Binary file not shown.
32 changes: 32 additions & 0 deletions test-components/large-dynamic-memory/c_api1.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
// Generated by `wit-bindgen` 0.26.0. DO NOT EDIT!
#include "c_api1.h"
#include <stdlib.h>

// Exported Functions from `c-api1`


// Canonical ABI intrinsics

__attribute__((__weak__, __export_name__("cabi_realloc")))
void *cabi_realloc(void *ptr, size_t old_size, size_t align, size_t new_size) {
(void) old_size;
if (new_size == 0) return (void*) align;
void *ret = realloc(ptr, new_size);
if (!ret) abort();
return ret;
}

// Component Adapters

__attribute__((__export_name__("run")))
int64_t __wasm_export_c_api1_run(void) {
uint64_t ret = c_api1_run();
return (int64_t) (ret);
}

// Ensure that the *_component_type.o object is linked in

extern void __component_type_object_force_link_c_api1(void);
void __component_type_object_force_link_c_api1_public_use_in_this_compilation_unit(void) {
__component_type_object_force_link_c_api1();
}
17 changes: 17 additions & 0 deletions test-components/large-dynamic-memory/c_api1.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
// Generated by `wit-bindgen` 0.26.0. DO NOT EDIT!
#ifndef __BINDINGS_C_API1_H
#define __BINDINGS_C_API1_H
#ifdef __cplusplus
extern "C" {
#endif

#include <stdint.h>
#include <stdbool.h>

// Exported Functions from `c-api1`
uint64_t c_api1_run(void);

#ifdef __cplusplus
}
#endif
#endif
Binary file not shown.
Loading

0 comments on commit 9904f42

Please sign in to comment.