<jupyter_start><jupyter_text>Single Container Model DevelopmentIn this notebook, our goal is to develop the necessary model for a single containerwith varying concurrency levels.<jupyter_code>%load_ext autoreload
%autoreload 2
# imports

# important libraries
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
import seaborn as sns
import scipy.stats as stats
import scipy as sp

from tqdm.auto import tqdm

# for better printing of variables
from IPython.display import display
# custom imports
from concperf import single_model, general_model
from concperf import utility<jupyter_output><empty_output><jupyter_text>Introduction Methods For Calculating Concurrency Distribution Method 1: Assuming Homogeneous ClusterIn this method, we assume a homogeneous cluster and thus the arrival rate for each server is $\lambda/N$ and solving forthe steady-state $\pi$ of a single container solves it for all of them. The main issue with this approach is thatit doesn't capture the synchronous behaviour of the servers as the load balancer wouldn't let one server to be overwhelmedwhile we have idle servers. To address this, we might want to do the second method.The main strong point of this method is that it captures the effect of concurrency on the service rate completely.This is very important as for most workloads with $CC>1$ we would want to have different service rates on differentconcurrency values. Because this seems to be very important for now, we will go ahead with this method until we decidethat the merits of the second method are woth more. Method 2: Single Chain For The ClusterIn this method, we use a single chain for the whole cluster. This way, we capture the synchronized behaviour of the clusterwith more fidelity, but we lose our ability to work on the change in service rate with different concurrency values.  Calculating Concurrency Distribution using Method 1<jupyter_code># update configuration dictionary
def update_config(config):
    config['arrival_rate_server'] = config['arrival_rate_total'] / config['instance_count']
    config['base_service_time'] = config['base_service_time_ms'] / 1000

single_container_config = {
    'instance_count': 10,
    'max_conc': 10,
    'arrival_rate_total': 5,
    'alpha': 0.11,
    'base_service_time_ms': 1154,
    'max_container_count': 25,
    'target_conc': 0.7, # assumes target utilization
    'max_scale_up_rate': 1000, # from N to 1000*N at most
    'max_scale_down_rate': 2, # from N to N/2 at most
}

# update the config
update_config(single_container_config)
display(single_container_config)

single_coder = single_model.StateCoder(config=single_container_config)

# calculate and show Q
Q = single_model.get_single_container_q(single_coder, config=single_container_config)
display(pd.DataFrame(Q))

req_count_prob = utility.solve_CTMC(Q)
# display solution for debug purposes
# display([f"{s:4.2f}" for s in req_count_prob])

# calculate average and deviation
req_df = pd.DataFrame(data = {
    'req_count': [s[0] for s in single_coder.get_state_list()],
    'req_count_prob': req_count_prob,
})
display(req_df)
# calculate average concurrency
req_count_mean, req_count_std = utility.get_normal_dist_params(vals=req_df['req_count'], probs=req_df['req_count_prob'])
utility.plot_normal_fit(vals=req_df['req_count'], probs=req_df['req_count_prob'], avg_count=10)
# req_count_avg = (req_df['req_count'] * req_df['req_count_prob']).sum()
print(f"Concurrency: {req_count_mean}(avg) +- {req_count_std}(std)")<jupyter_output>Concurrency: 0.6161040935667044(avg) +- 0.8110844781803102(std)<jupyter_text>Comparing Calculated Normal Fit With Other Calculations<jupyter_code># plot initial values
plt.figure(figsize=(8,4))
plt.bar(req_df['req_count'], req_df['req_count_prob'])
avg_count = 300

import time
start_time = time.time()
req_count_averaged_vals, req_count_averaged_probs = utility.get_averaged_distribution(vals=req_df['req_count'], probs=req_df['req_count_prob'], avg_count=avg_count)
print(f"Calculation took {time.time() - start_time} seconds")

plt.figure(figsize=(8,4))
plt.bar(req_count_averaged_vals, req_count_averaged_probs, width=1/avg_count)
plt.xlim([-1,3])<jupyter_output>Calculation took 0.4550914764404297 seconds<jupyter_text>Calculate Ordered Containers<jupyter_code>new_order_val, new_order_prob = general_model.get_new_order_dist(req_count_averaged_vals, req_count_averaged_probs, single_container_config)

# plot the result
plt.bar(new_order_val, new_order_prob, width=1)

# show results as pandas dataframe
display(pd.DataFrame(data = {
    'new_order_val': new_order_val,
    'new_order_prob': new_order_prob,
}))<jupyter_output><empty_output><jupyter_text>Checking Edge CaseHere, we check very low arrival rate case just to be sure everything is on track.<jupyter_code># checking edge case
single_container_config.update({
    'instance_count': 1,
    'arrival_rate_total': .01,
})

update_config(single_container_config)

# calculate and show Q
Q = single_model.get_single_container_q(single_coder, config=single_container_config)
req_count_prob = utility.solve_CTMC(Q)
# calculate average and deviation
req_df = pd.DataFrame(data = {
    'req_count': [s[0] for s in single_coder.get_state_list()],
    'req_count_prob': req_count_prob,
})
req_count_averaged_vals, req_count_averaged_probs = utility.get_averaged_distribution(vals=req_df['req_count'], probs=req_df['req_count_prob'], avg_count=avg_count)
new_order_val, new_order_prob = general_model.get_new_order_dist(req_count_averaged_vals, req_count_averaged_probs, single_container_config)

# plot the result
plt.bar(new_order_val, new_order_prob, width=1)
# checking another edge case
single_container_config.update({
    'instance_count': 2,
    'arrival_rate_total': 15,
    'alpha': 1,
    'base_service_time_ms': 200,
    'target_conc': 16*0.7, # assumes target utilization
    'max_conc': 100,
})

update_config(single_container_config)

# update state coder
single_coder = single_model.StateCoder(config=single_container_config)

# calculate and show Q
Q = single_model.get_single_container_q(single_coder, config=single_container_config)
req_count_prob = utility.solve_CTMC(Q)
# calculate average and deviation
req_df = pd.DataFrame(data = {
    'req_count': [s[0] for s in single_coder.get_state_list()],
    'req_count_prob': req_count_prob,
})
req_count_averaged_vals, req_count_averaged_probs = utility.get_averaged_distribution(vals=req_df['req_count'], probs=req_df['req_count_prob'], avg_count=avg_count)
new_order_val, new_order_prob = general_model.get_new_order_dist(req_count_averaged_vals, req_count_averaged_probs, single_container_config)

# plot the result
plt.bar(new_order_val, new_order_prob, width=1)<jupyter_output><empty_output><jupyter_text>Plot Average Concurrency vs. Arrival Rate Per Container<jupyter_code>single_container_config = {
    'instance_count': 1,
    'max_conc': 10,
    'alpha': 0.03,
    'base_service_time_ms': 1500,
}

res = []
for arrival_rate in np.linspace(0.5, 3.5, 7):
    single_container_config.update({
        'arrival_rate_total': arrival_rate,
    })

    update_config(single_container_config)

    # calculate and show Q
    Q = single_model.get_single_container_q(single_coder, config=single_container_config)
    req_count_prob = utility.solve_CTMC(Q)
    # calculate average and deviation
    req_df = pd.DataFrame(data = {
        'req_count': [s[0] for s in single_coder.get_state_list()],
        'req_count_prob': req_count_prob,
    })

    mean_conc = (req_df['req_count'] * req_df['req_count_prob']).sum()

    res.append({
        'arrival_rate': arrival_rate,
        'mean_conc': mean_conc,
    })

tmpdf = pd.DataFrame(data=res)
tmpdf.head()

plt.plot(tmpdf['arrival_rate'], tmpdf['mean_conc'])<jupyter_output><empty_output>
{'fraction_non_alphanumeric': 0.0926497044461578, 'fraction_numerical': 0.017861732202518632, 'mean_word_length': 6.419447092469018, 'pattern_counts': {'":': 0, '<': 23, '<?xml version=': 0, '>': 24, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 1, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'chain_length': '6', 'dir': 'jupyter-structured-clean-dedup', 'id': '644331', 'license': 'MIT', 'n_tokens_mistral': 2697, 'n_tokens_neox': 2552, 'n_words': 635, 'path': 'model/01-calculate-params.ipynb', 'repo_name': 'pacslab/conc-value-perf-modelling', 'size': '7782'}