Skip to content
Snippets Groups Projects
Commit 620f9e62 authored by Cyril Moineau's avatar Cyril Moineau
Browse files

Merge branch 'meminfo_plot' into 'dev'

Rewrite memory manager log_info in Python.

See merge request !276
parents a983179d beaff309
No related branches found
No related tags found
2 merge requests!318[Upd] release verision 0.5.0,!276Rewrite memory manager log_info in Python.
Pipeline #62350 passed
......@@ -5,6 +5,9 @@ from pathlib import Path
import aidge_core
from typing import Tuple, List
import matplotlib.pyplot as plt
import aidge_core.mem_info
import numpy as np
# Default memory management, which can be used for development
def compute_default_mem_info(scheduler: aidge_core.Scheduler) -> Tuple[int, List]:
......@@ -41,20 +44,72 @@ def compute_default_mem_info(scheduler: aidge_core.Scheduler) -> Tuple[int, List
mem_info[node] = [] # No meminfo for producer
return mem_size, mem_info
def log_meminfo(mem_manager:aidge_core.MemoryManager, path: Path, diplay_names:bool):
"""Generate a graph representing the memory allocation of each ouputs.
def _gnuplot_installed():
try:
# Run gnuplot with the --version flag and capture the output
subprocess.run(["gnuplot", "--version"])
return True
except FileNotFoundError:
aidge_core.Log.warn("Gnuplot is not installed.")
return False
except subprocess.CalledProcessError:
aidge_core.Log.warn("Gnuplot command found but failed to run.")
return False
Block with the smae color correspond to the same memory plane.
def generate_optimized_memory_info(scheduler: aidge_core.Scheduler, stats_folder: Path = None, wrapping: bool = False) -> Tuple[int, List[dict]]:
:param mem_manager: Memory manager to log
:type mem_manager: aidge_core.memory_manager
:param path: Path where to save the figure
:type path: Path
:param diplay_names: If True Node names are diplayed alongside their block
:type diplay_names: bool
"""
max_lifetime = mem_manager.get_max_lifetime()
# peak_usage in kwords
peak_usage = mem_manager.get_peak_usage() / 1024
# Set figure size 1920x1080 px
plt.figure(figsize=(19.20, 10.80))
# Same color for each planes
colors = plt.cm.viridis(np.linspace(0, 1, len(mem_manager.get_planes()) + 1))
color_id = 1
for node, planes in mem_manager.get_planes().items():
for plane in planes:
cont_offset = plane.get_contiguous_offset()
cont_size = plane.get_contiguous_size()
allocated = plane.mem_space.allocated
released = plane.mem_space.released
is_released = released >= 0 and not plane.mem_space.dependencies
x_start = allocated
y_start = cont_offset / 1024.0
y_end = (cont_offset + cont_size) / 1024.0
x_end = max_lifetime if not is_released else released
plt.fill_betweenx(
[y_start, y_end],
x_start,
x_end + 1,
color=colors[color_id % len(colors)]
)
if diplay_names:
# Rotation for lisibility!
plt.text(x_end,y_end, node.name(), rotation=45)
color_id += 1
plt.xlim(0, max_lifetime + 1)
plt.ylim(0, peak_usage)
plt.axhline(y=peak_usage, color='red', linestyle='--')
plt.text(0, peak_usage, f'Peak usage = {peak_usage} KWords', color='red')
plt.xlabel("Time")
plt.ylabel("Memory usage (KWords)")
plt.title("Memory Usage Over Time")
plt.grid(True)
ax = plt.gca()
ax.spines['top'].set_visible(False)
ax.spines['right'].set_visible(False)
folder_path = path.parent
folder_path.mkdir(parents=True, exist_ok=True)
plt.savefig(path)
plt.close()
aidge_core.Log.notice(f"Generated memory management info at: {path}")
def generate_optimized_memory_info(scheduler: aidge_core.Scheduler, stats_folder: Path = None, wrapping: bool = False, display_names: bool=True) -> Tuple[int, List[dict]]:
"""Generates optimized memory information for a computation graph managed by a scheduler.
This function analyzes the memory usage of a computation graph, determining the memory peak
......@@ -70,6 +125,8 @@ def generate_optimized_memory_info(scheduler: aidge_core.Scheduler, stats_folder
:param wrapping: Boolean flag to enable or disable wrap-around buffer optimization.
Defaults to `False`.
:type wrapping: bool, optional
:param diplay_names: If True Node names are diplayed in the memory plot alongside their block, defaults to False
:type diplay_names: bool, optional
:return: A tuple containing the peak memory size and a list of memory information for each
scheduled node. The memory information for each node includes details such as size,
offset, stride, length, count, and optional wrap-around details.
......@@ -88,18 +145,8 @@ def generate_optimized_memory_info(scheduler: aidge_core.Scheduler, stats_folder
nodes_at_input = [n[0] for n in scheduler.graph_view().inputs()]
if stats_folder is not None:
if _gnuplot_installed():
# Use gnuplot to generate the log
os.makedirs(str(Path(stats_folder) / "graph"), exist_ok=True)
mem_manager.log("memory_info")
os.chmod("memory_info_plot.gnu", 0o777)
os.system("./memory_info_plot.gnu")
shutil.move("memory_info", str(Path(stats_folder) / "graph" / "memory_info"))
shutil.move("memory_info_plot.png", str(
Path(stats_folder) / "graph" / "memory_info_plot.png"))
os.remove("memory_info_plot.gnu")
else:
aidge_core.Log.warn("Warning: gnuplot is not installed, could not generate stat folder.")
log_meminfo(mem_manager, Path(stats_folder) / "memory_info.png", display_names)
# In the export, we currently use an unified memory buffer whose size
# is determined by the memory peak usage
mem_size = mem_manager.get_peak_usage()
......
......@@ -4,7 +4,8 @@ name="aidge_core"
description="Core algorithms for operators and graph of the AIDGE framework"
dependencies = [
"numpy>=1.21.6",
"Jinja2>=3.1.2"
"Jinja2>=3.1.2",
"matplotlib"
]
requires-python = ">= 3.7"
readme = "README.md"
......
......@@ -36,10 +36,10 @@ void init_MemoryManager(py::module& m)
.def_readwrite("released", &MemoryManager::MemorySpace::released);
py::class_<MemoryManager::MemoryPlane, std::shared_ptr<MemoryManager::MemoryPlane>>(m, "MemoryPlane")
.def(py::init<std::shared_ptr<MemoryManager::MemorySpace>,
.def(py::init<std::shared_ptr<MemoryManager::MemorySpace>,
MemoryManager::Clock_T, unsigned int, unsigned int,
unsigned int, unsigned int, unsigned int>(),
py::arg("mem_space"), py::arg("clock"), py::arg("offset"),
py::arg("mem_space"), py::arg("clock"), py::arg("offset"),
py::arg("size"), py::arg("stride"), py::arg("length"), py::arg("count"))
.def_readwrite("mem_space", &MemoryManager::MemoryPlane::memSpace)
.def_readwrite("allocated", &MemoryManager::MemoryPlane::allocated)
......@@ -101,7 +101,6 @@ void init_MemoryManager(py::module& m)
.def("get_nb_planes", (unsigned int (MemoryManager::*)(std::shared_ptr<MemoryManager::MemorySpace>) const) &MemoryManager::getNbPlanes, py::arg("mem_space"))
.def("get_current_tick", &MemoryManager::getCurrentTick)
.def("tick", &MemoryManager::tick)
.def("log", &MemoryManager::log, py::arg("file_name"))
;
}
......
......@@ -634,152 +634,6 @@ void Aidge::MemoryManager::tick()
++mClock;
}
void Aidge::MemoryManager::log(const std::string& fileName) const
{
auto memData = std::unique_ptr<FILE, decltype(&std::fclose)>(std::fopen(fileName.c_str(), "w"), &std::fclose);
if (!memData) {
AIDGE_THROW_OR_ABORT(std::runtime_error,
"Could not create memory layout log file: {}", fileName);
}
auto gnuplot = std::unique_ptr<FILE, decltype(&std::fclose)>(std::fopen((fileName + "_plot.gnu").c_str(), "w"), &std::fclose);
if (!gnuplot) {
AIDGE_THROW_OR_ABORT(std::runtime_error,
"Could not create memory layout log file: {}", (fileName + "_plot.gnu"));
}
const Clock_T maxLifetime = getMaxLifetime();
const unsigned int peakUsage = getPeakUsage();
fmt::print(gnuplot.get(), "#!/usr/bin/gnuplot\n");
fmt::print(gnuplot.get(), "set term pngcairo size 1280,768 noenhanced\n");
fmt::print(gnuplot.get(), "set output \"{}\"\n", fileName + "_plot.png");
fmt::print(gnuplot.get(), "set xrange [{}:{}]\n", 0, maxLifetime + 1);
fmt::print(gnuplot.get(), "set yrange [{}:{}]\n", 0, 1.05 * (peakUsage / 1024.0));
fmt::print(gnuplot.get(), "set xlabel \"Time\"\n");
fmt::print(gnuplot.get(), "set ylabel \"Memory usage (KWords)\"\n");
fmt::print(gnuplot.get(), "set grid\n");
fmt::print(gnuplot.get(), "set xtics 1\n");
fmt::print(gnuplot.get(), "unset key\n");
fmt::print(gnuplot.get(), "set palette rgbformulae 30,31,32\n");
fmt::print(gnuplot.get(), "unset colorbox\n");
fmt::print(gnuplot.get(), "N={}\n", mMemPlanes.size() + 1);
unsigned int objectId = 1;
unsigned int labelId = 1;
for (std::map<std::shared_ptr<Node>, std::vector<MemoryPlane> >
::const_iterator it = mMemPlanes.begin(), itEnd = mMemPlanes.end();
it != itEnd; ++it)
{
const std::string name = (*it).first->name();
fmt::print(memData.get(), "{}\n", name);
double minX = -1;
unsigned int maxY = 0;
for (std::vector<MemoryPlane>::const_iterator itPlanes
= (*it).second.begin(), itPlanesBegin = (*it).second.begin(),
itPlanesEnd = (*it).second.end(); itPlanes != itPlanesEnd;
++itPlanes)
{
const unsigned int contiguousOffset
= (*itPlanes).getContiguousOffset();
const unsigned int contiguousSize = (*itPlanes).getContiguousSize();
const unsigned int wrappedOffset = (*itPlanes).getWrappedOffset();
const unsigned int wrappedSize = (*itPlanes).getWrappedSize();
const Clock_T allocated = (*itPlanes).allocated;
const Clock_T released = (*itPlanes).memSpace->released;
const bool isReleased = (released >= 0
&& (*itPlanes).memSpace->dependencies.empty());
fmt::print(memData.get(), " {} {} ({:#08x}U) -> {} ({:#08x}U)",
(itPlanes - itPlanesBegin), contiguousOffset, contiguousOffset,
(contiguousOffset + contiguousSize), (contiguousOffset + contiguousSize));
if (wrappedSize > 0) {
fmt::print(memData.get(), " + {} ({:#08x}U) -> {} ({:#08x}U)",
wrappedOffset, wrappedOffset,
(wrappedOffset + wrappedSize), (wrappedOffset + wrappedSize));
}
fmt::print(memData.get(), " [{}] @ {}", (*itPlanes).getSize(), allocated);
if (isReleased) {
fmt::print(memData.get(), " to {}", released);
}
fmt::print(memData.get(), "\n");
// Gnuplot
const double startX = allocated;
if (startX < minX || minX < 0) {
minX = startX;
maxY = contiguousOffset + contiguousSize;
}
if ((*itPlanes).size != (*itPlanes).stride) {
for (unsigned int offset = contiguousOffset;
offset < contiguousOffset + contiguousSize;
offset += (*itPlanes).stride)
{
fmt::print(gnuplot.get(), "set object {} rectangle from {},{} to {},{} fc palette frac ({} * 1./N)\n",
(allocated * 100 + objectId), startX, (offset / 1024.0),
(((isReleased) ? released : maxLifetime) + 1),
(std::min((offset + (*itPlanes).size),
contiguousOffset + contiguousSize) / 1024.0),
labelId);
++objectId;
}
}
else {
fmt::print(gnuplot.get(), "set object {} rectangle from {},{} to {},{} fc palette frac ({} * 1./N)\n",
(allocated * 100 + objectId), startX, (contiguousOffset / 1024.0),
(((isReleased) ? released : maxLifetime) + 1),
((contiguousOffset + contiguousSize) / 1024.0),
labelId);
++objectId;
}
if (wrappedSize > 0) {
fmt::print(gnuplot.get(), "set object {} rectangle from {},{} to {},{} fc palette frac ({} * 1./N)\n",
(allocated * 100 + objectId), startX, (wrappedOffset / 1024.0),
(((isReleased) ? released : maxLifetime) + 1),
((wrappedOffset + contiguousSize) / 1024.0),
labelId);
++objectId;
fmt::print(gnuplot.get(), "set arrow from {},{} to {},{} nohead\n",
startX, (contiguousOffset / 1024.0),
(startX + 0.1), (contiguousOffset / 1024.0));
fmt::print(gnuplot.get(), "set arrow from {},{} to {},{} nohead\n",
(startX + 0.05), ((contiguousOffset + contiguousSize) / 1024.0),
(startX + 0.05), (wrappedOffset / 1024.0));
}
}
fmt::print(gnuplot.get(), "set label {} '{}' at {},{} rotate by 30 font \",8\" offset char 0.5,0.5\n",
labelId, name, minX, (maxY / 1024.0));
++labelId;
fmt::print(memData.get(), "\n");
}
fmt::print(gnuplot.get(), "set arrow from 0,{} to {},{} nohead lc rgb \"red\"\n",
(peakUsage / 1024.0), (maxLifetime + 1),
(peakUsage / 1024.0));
fmt::print(gnuplot.get(), "set label {} 'Peak usage = {} KWords' at 0,{} textcolor rgb \"red\" offset char 0.5,0.5\n",
labelId, (peakUsage / 1024.0), (peakUsage / 1024.0));
fmt::print(gnuplot.get(), "plot 0\n");
}
unsigned int Aidge::MemoryManager::onStack(unsigned int size)
{
unsigned int offset = 0;
......
......@@ -136,7 +136,6 @@ TEST_CASE("allocate1", "[MemoryManager]") {
REQUIRE(memManager.getPlanes(node4).back().memSpace->allocated == 3);
REQUIRE(memManager.getPlanes(node4).back().memSpace->released == 4);
memManager.log("MemoryManager_allocate1.log");
}
TEST_CASE("allocate2", "[MemoryManager]") {
......@@ -281,7 +280,6 @@ TEST_CASE("allocate2", "[MemoryManager]") {
REQUIRE(memManager.getPlanes(node4).back().memSpace->allocated == 3);
REQUIRE(memManager.getPlanes(node4).back().memSpace->released == 4);
memManager.log("MemoryManager_allocate2.log");
}
TEST_CASE("allocate3", "[MemoryManager]") {
......@@ -438,7 +436,6 @@ TEST_CASE("allocate3", "[MemoryManager]") {
REQUIRE(memManager.getPlanes(node4).back().memSpace->allocated == 0);
REQUIRE(memManager.getPlanes(node4).back().memSpace->released == 4);
memManager.log("MemoryManager_allocate3.log");
}
TEST_CASE("allocate3_wrapAround", "[MemoryManager]") {
......@@ -595,5 +592,4 @@ TEST_CASE("allocate3_wrapAround", "[MemoryManager]") {
REQUIRE(memManager.getPlanes(node4).back().memSpace->allocated == 0);
REQUIRE(memManager.getPlanes(node4).back().memSpace->released == 4);
memManager.log("MemoryManager_allocate3_wrapAround.log");
}
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment