Merge branch 'develop' into rubyhitmiss
This commit is contained in:
10
.github/workflows/daily-tests.yaml
vendored
10
.github/workflows/daily-tests.yaml
vendored
@@ -61,7 +61,7 @@ jobs:
|
||||
run: scons setconfig build/${{ matrix.image }} ${{ matrix.setconfig-option }}
|
||||
- name: Build gem5
|
||||
run: scons build/${{ matrix.image }}/gem5.opt -j $(nproc)
|
||||
- uses: actions/upload-artifact@v4
|
||||
- uses: actions/upload-artifact@v4.0.0
|
||||
with:
|
||||
name: ${{ needs.name-artifacts.outputs.build-name }}${{ matrix.image }}
|
||||
path: '*build/${{ matrix.image }}/gem5.opt'
|
||||
@@ -125,7 +125,7 @@ jobs:
|
||||
# download all artifacts for each test. Thoguh this is inelegant,
|
||||
# it's simpler than figuring otu which long tests requires which
|
||||
# binary.
|
||||
- uses: actions/download-artifact@v4
|
||||
- uses: actions/download-artifact@v4.0.0
|
||||
with:
|
||||
name: ${{needs.name-artifacts.outputs.build-name}}
|
||||
# The upload/download GitHub actions do not preserve the executable
|
||||
@@ -138,7 +138,7 @@ jobs:
|
||||
run: ./main.py run gem5/${{ matrix.test-type }} --length=long --skip-build -vv -t $(nproc)
|
||||
- name: upload results
|
||||
if: success() || failure()
|
||||
uses: actions/upload-artifact@v4
|
||||
uses: actions/upload-artifact@v4.0.0
|
||||
env:
|
||||
MY_STEP_VAR: ${{ matrix.test-type }}_COMMIT.${{github.sha}}_RUN.${{github.run_id}}_ATTEMPT.${{github.run_attempt}}
|
||||
with:
|
||||
@@ -167,7 +167,7 @@ jobs:
|
||||
# Scheduled workflows run on the default branch by default. We
|
||||
# therefore need to explicitly checkout the develop branch.
|
||||
ref: develop
|
||||
- uses: actions/download-artifact@v4
|
||||
- uses: actions/download-artifact@v4.0.0
|
||||
with:
|
||||
name: ${{needs.name-artifacts.outputs.build-name}}ALL
|
||||
- run: chmod u+x build/ALL/gem5.opt
|
||||
@@ -177,7 +177,7 @@ jobs:
|
||||
--skip-build -vv
|
||||
- name: upload results
|
||||
if: success() || failure()
|
||||
uses: actions/upload-artifact@v4
|
||||
uses: actions/upload-artifact@v4.0.0
|
||||
env:
|
||||
MY_STEP_VAR: ${{ matrix.test-type }}_COMMIT.${{github.sha}}_RUN.${{github.run_id}}_ATTEMPT.${{github.run_attempt}}
|
||||
with:
|
||||
|
||||
@@ -720,7 +720,7 @@ TEST(LoaderSymtabTest, FindNearestRoundWithNext)
|
||||
EXPECT_TRUE(symtab.insert(symbols[0]));
|
||||
EXPECT_TRUE(symtab.insert(symbols[1]));
|
||||
|
||||
Addr next_addr;
|
||||
Addr next_addr = 0;
|
||||
const auto it = symtab.findNearest(symbols[0].address() + 0x1, next_addr);
|
||||
ASSERT_NE(it, symtab.end());
|
||||
ASSERT_PRED_FORMAT2(checkSymbol, *it, symbols[0]);
|
||||
@@ -741,7 +741,7 @@ TEST(LoaderSymtabTest, FindNearestRoundWithNextNonExistent)
|
||||
"symbol", 0x10};
|
||||
EXPECT_TRUE(symtab.insert(symbol));
|
||||
|
||||
Addr next_addr;
|
||||
Addr next_addr = 0;
|
||||
const auto it = symtab.findNearest(symbol.address() + 0x1, next_addr);
|
||||
ASSERT_NE(it, symtab.end());
|
||||
ASSERT_PRED_FORMAT2(checkSymbol, *it, symbol);
|
||||
|
||||
@@ -86,9 +86,7 @@ ThreadContext::suspend()
|
||||
DPRINTF(O3CPU, "Calling suspend on Thread Context %d\n",
|
||||
threadId());
|
||||
|
||||
if (thread->status() == gem5::ThreadContext::Suspended ||
|
||||
thread->status() == gem5::ThreadContext::Halting ||
|
||||
thread->status() == gem5::ThreadContext::Halted)
|
||||
if (thread->status() == gem5::ThreadContext::Suspended)
|
||||
return;
|
||||
|
||||
if (cpu->isDraining()) {
|
||||
|
||||
@@ -143,8 +143,7 @@ SimpleThread::activate()
|
||||
void
|
||||
SimpleThread::suspend()
|
||||
{
|
||||
if (status() == ThreadContext::Suspended ||
|
||||
status() == ThreadContext::Halted)
|
||||
if (status() == ThreadContext::Suspended)
|
||||
return;
|
||||
|
||||
lastActivate = curTick();
|
||||
|
||||
@@ -606,6 +606,7 @@ machine(MachineType:TCC, "TCC Cache")
|
||||
out_msg.Destination.add(in_msg.Requestor);
|
||||
out_msg.Sender := machineID;
|
||||
out_msg.MessageSize := MessageSizeType:Response_Data;
|
||||
out_msg.instSeqNum := in_msg.instSeqNum;
|
||||
out_msg.DataBlk := cache_entry.DataBlk;
|
||||
out_msg.isGLCSet := in_msg.isGLCSet;
|
||||
out_msg.isSLCSet := in_msg.isSLCSet;
|
||||
@@ -1301,6 +1302,7 @@ machine(MachineType:TCC, "TCC Cache")
|
||||
wardb_writeAtomicResponseDirtyBytes;
|
||||
pa_performAtomic;
|
||||
baplr_sendBypassedAtomicPerformedLocallyResponse;
|
||||
wada_wakeUpAllDependentsAddr;
|
||||
dt_deallocateTBE;
|
||||
pr_popResponseQueue;
|
||||
}
|
||||
|
||||
@@ -25,21 +25,21 @@
|
||||
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
from .abstract_stat import AbstractStat
|
||||
from .group import Group
|
||||
from .group import (
|
||||
Group,
|
||||
SimObjectGroup,
|
||||
SimObjectVectorGroup,
|
||||
)
|
||||
from .jsonloader import JsonLoader
|
||||
from .serializable_stat import SerializableStat
|
||||
from .simstat import SimStat
|
||||
from .statistic import Statistic
|
||||
from .statistic import (
|
||||
Distribution,
|
||||
Scalar,
|
||||
SparseHist,
|
||||
Statistic,
|
||||
Vector,
|
||||
Vector2d,
|
||||
)
|
||||
from .storagetype import StorageType
|
||||
from .timeconversion import TimeConversion
|
||||
|
||||
__all__ = [
|
||||
"AbstractStat",
|
||||
"Group",
|
||||
"SimStat",
|
||||
"Statistic",
|
||||
"TimeConversion",
|
||||
"StorageType",
|
||||
"SerializableStat",
|
||||
"JsonLoader",
|
||||
]
|
||||
|
||||
@@ -26,10 +26,12 @@
|
||||
|
||||
import re
|
||||
from typing import (
|
||||
Any,
|
||||
Callable,
|
||||
List,
|
||||
Optional,
|
||||
Pattern,
|
||||
Tuple,
|
||||
Union,
|
||||
)
|
||||
|
||||
@@ -60,20 +62,11 @@ class AbstractStat(SerializableStat):
|
||||
If it returns ``True``, then the child is yielded.
|
||||
Otherwise, the child is skipped. If not provided then
|
||||
all children are returned.
|
||||
|
||||
Note: This is method must be implemented in AbstractStat subclasses
|
||||
which have children, otherwise it will return an empty list.
|
||||
"""
|
||||
|
||||
to_return = []
|
||||
for attr in self.__dict__:
|
||||
obj = getattr(self, attr)
|
||||
if isinstance(obj, AbstractStat):
|
||||
if (predicate and predicate(attr)) or not predicate:
|
||||
to_return.append(obj)
|
||||
if recursive:
|
||||
to_return = to_return + obj.children(
|
||||
predicate=predicate, recursive=True
|
||||
)
|
||||
|
||||
return to_return
|
||||
return []
|
||||
|
||||
def find(self, regex: Union[str, Pattern]) -> List["AbstractStat"]:
|
||||
"""Find all stats that match the name, recursively through all the
|
||||
@@ -99,3 +92,52 @@ class AbstractStat(SerializableStat):
|
||||
return self.children(
|
||||
lambda _name: re.match(pattern, _name), recursive=True
|
||||
)
|
||||
|
||||
def _get_vector_item(self, item: str) -> Optional[Tuple[str, int, Any]]:
|
||||
"""It has been the case in gem5 that SimObject vectors are stored as
|
||||
strings such as "cpu0" or "cpu1". This function splits the string into
|
||||
the SimObject name and index, (e.g.: ["cpu", 0] and ["cpu", 1]) and
|
||||
returns the item for that name and it's index. If the string cannot be
|
||||
split into a SimObject name and index, or if the SimObject does not
|
||||
exit at `Simobject[index]`, the function returns None.
|
||||
"""
|
||||
regex = re.compile("[0-9]+$")
|
||||
match = regex.search(item)
|
||||
if not match:
|
||||
return None
|
||||
|
||||
match_str = match.group()
|
||||
|
||||
assert match_str.isdigit(), f"Regex match must be a digit: {match_str}"
|
||||
vector_index = int(match_str)
|
||||
vector_name = item[: (-1 * len(match_str))]
|
||||
|
||||
if hasattr(self, vector_name):
|
||||
vector = getattr(self, vector_name)
|
||||
try:
|
||||
vector_value = vector[vector_index]
|
||||
return vector_name, vector_index, vector_value
|
||||
except KeyError:
|
||||
pass
|
||||
return None
|
||||
|
||||
def __iter__(self):
|
||||
return iter(self.__dict__)
|
||||
|
||||
def __getattr__(self, item: str) -> Any:
|
||||
vector_item = self._get_vector_item(item)
|
||||
if not vector_item:
|
||||
return None
|
||||
|
||||
assert (
|
||||
len(vector_item) == 3
|
||||
), f"Vector item must have 3 elements: {vector_item}"
|
||||
return vector_item[2]
|
||||
|
||||
def __getitem__(self, item: str):
|
||||
return getattr(self, item)
|
||||
|
||||
def __contains__(self, item: Any) -> bool:
|
||||
return (
|
||||
isinstance(item, str) and self._get_vector_item(item)
|
||||
) or hasattr(self, item)
|
||||
|
||||
@@ -25,18 +25,16 @@
|
||||
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
from typing import (
|
||||
Any,
|
||||
Callable,
|
||||
Dict,
|
||||
List,
|
||||
Mapping,
|
||||
Optional,
|
||||
Union,
|
||||
)
|
||||
|
||||
from .abstract_stat import AbstractStat
|
||||
from .statistic import (
|
||||
Scalar,
|
||||
Statistic,
|
||||
)
|
||||
from .statistic import Statistic
|
||||
from .timeconversion import TimeConversion
|
||||
|
||||
|
||||
@@ -57,9 +55,7 @@ class Group(AbstractStat):
|
||||
str, Union["Group", Statistic, List["Group"], List["Statistic"]]
|
||||
],
|
||||
):
|
||||
if type is None:
|
||||
self.type = "Group"
|
||||
else:
|
||||
if type:
|
||||
self.type = type
|
||||
|
||||
self.time_conversion = time_conversion
|
||||
@@ -67,18 +63,70 @@ class Group(AbstractStat):
|
||||
for key, value in kwargs.items():
|
||||
setattr(self, key, value)
|
||||
|
||||
def children(
|
||||
self,
|
||||
predicate: Optional[Callable[[str], bool]] = None,
|
||||
recursive: bool = False,
|
||||
) -> List["AbstractStat"]:
|
||||
to_return = []
|
||||
for attr in self.__dict__:
|
||||
obj = getattr(self, attr)
|
||||
if isinstance(obj, AbstractStat):
|
||||
if (predicate and predicate(attr)) or not predicate:
|
||||
to_return.append(obj)
|
||||
if recursive:
|
||||
to_return = to_return + obj.children(
|
||||
predicate=predicate, recursive=True
|
||||
)
|
||||
return to_return
|
||||
|
||||
class Vector(Group):
|
||||
"""
|
||||
The Vector class is used to store vector information. However, in gem5
|
||||
Vectors, in practise, hold information that is more like a dictionary of
|
||||
Scalar Values. This class may change, and may be merged into Group in
|
||||
accordance to decisions made in relation to
|
||||
https://gem5.atlassian.net/browse/GEM5-867.
|
||||
|
||||
class SimObjectGroup(Group):
|
||||
"""A group of statistics encapulated within a SimObject."""
|
||||
|
||||
def __init__(self, **kwargs: Dict[str, Union[Group, Statistic]]):
|
||||
super().__init__(type="SimObject", **kwargs)
|
||||
|
||||
|
||||
class SimObjectVectorGroup(Group):
|
||||
"""A Vector of SimObject objects. I.e., that which would be constructed
|
||||
from something like `system.cpu = [DerivO3CPU(), TimingSimpleCPU()]`.
|
||||
"""
|
||||
|
||||
def __init__(self, scalar_map: Mapping[str, Scalar]):
|
||||
super().__init__(type="Vector", time_conversion=None, **scalar_map)
|
||||
def __init__(self, value: List[AbstractStat], **kwargs: Dict[str, Any]):
|
||||
assert isinstance(value, list), "Value must be a list"
|
||||
super().__init__(type="SimObjectVector", value=value, **kwargs)
|
||||
|
||||
def _repr_name(self) -> str:
|
||||
return "Vector"
|
||||
def __getitem__(self, index: Union[int, str, float]) -> AbstractStat:
|
||||
if not isinstance(index, int):
|
||||
raise KeyError(
|
||||
f"Index {index} not found in int. Cannot index Array with "
|
||||
"non-int"
|
||||
)
|
||||
return self.value[index]
|
||||
|
||||
def __iter__(self):
|
||||
return iter(self.value)
|
||||
|
||||
def __len__(self):
|
||||
return len(self.value)
|
||||
|
||||
def __getitem__(self, item: int):
|
||||
return self.value[item]
|
||||
|
||||
def __contains__(self, item):
|
||||
if isinstance(item, int):
|
||||
return item >= 0 and item < len(self)
|
||||
|
||||
def children(
|
||||
self,
|
||||
predicate: Optional[Callable[[str], bool]] = None,
|
||||
recursive: bool = False,
|
||||
) -> List["AbstractStat"]:
|
||||
to_return = []
|
||||
for child in self.value:
|
||||
to_return = to_return + child.children(
|
||||
predicate=predicate, recursive=recursive
|
||||
)
|
||||
|
||||
return to_return
|
||||
|
||||
@@ -31,13 +31,9 @@ from typing import (
|
||||
Union,
|
||||
)
|
||||
|
||||
from .group import (
|
||||
Group,
|
||||
Vector,
|
||||
)
|
||||
from .group import Group
|
||||
from .simstat import SimStat
|
||||
from .statistic import (
|
||||
Accumulator,
|
||||
Distribution,
|
||||
Scalar,
|
||||
Statistic,
|
||||
@@ -74,10 +70,6 @@ class JsonLoader(json.JSONDecoder):
|
||||
d.pop("type", None)
|
||||
return Distribution(**d)
|
||||
|
||||
elif d["type"] == "Accumulator":
|
||||
d.pop("type", None)
|
||||
return Accumulator(**d)
|
||||
|
||||
elif d["type"] == "Group":
|
||||
return Group(**d)
|
||||
|
||||
|
||||
@@ -85,6 +85,11 @@ class SerializableStat:
|
||||
return value
|
||||
elif isinstance(value, datetime):
|
||||
return value.replace(microsecond=0).isoformat()
|
||||
elif isinstance(value, Dict):
|
||||
d = {}
|
||||
for k, v in value.items():
|
||||
d[self.__process_json_value(k)] = self.__process_json_value(v)
|
||||
return d
|
||||
elif isinstance(value, list):
|
||||
return [self.__process_json_value(v) for v in value]
|
||||
elif isinstance(value, StorageType):
|
||||
|
||||
@@ -32,22 +32,16 @@ from typing import (
|
||||
Union,
|
||||
)
|
||||
|
||||
from .abstract_stat import AbstractStat
|
||||
from .group import Group
|
||||
from .statistic import Statistic
|
||||
from .timeconversion import TimeConversion
|
||||
|
||||
|
||||
class SimStat(AbstractStat):
|
||||
class SimStat(Group):
|
||||
"""
|
||||
Contains all the statistics for a given simulation.
|
||||
"""
|
||||
|
||||
creation_time: Optional[datetime]
|
||||
time_conversion: Optional[TimeConversion]
|
||||
simulated_begin_time: Optional[Union[int, float]]
|
||||
simulated_end_time: Optional[Union[int, float]]
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
creation_time: Optional[datetime] = None,
|
||||
@@ -56,10 +50,10 @@ class SimStat(AbstractStat):
|
||||
simulated_end_time: Optional[Union[int, float]] = None,
|
||||
**kwargs: Dict[str, Union[Group, Statistic, List[Group]]]
|
||||
):
|
||||
self.creation_time = creation_time
|
||||
self.time_conversion = time_conversion
|
||||
self.simulated_begin_time = simulated_begin_time
|
||||
self.simulated_end_time = simulated_end_time
|
||||
|
||||
for key, value in kwargs.items():
|
||||
setattr(self, key, value)
|
||||
super().__init__(
|
||||
creation_time=creation_time,
|
||||
time_conversion=time_conversion,
|
||||
simulated_begin_time=simulated_begin_time,
|
||||
simulated_end_time=simulated_end_time,
|
||||
**kwargs
|
||||
)
|
||||
|
||||
@@ -27,6 +27,8 @@
|
||||
from abc import ABC
|
||||
from typing import (
|
||||
Any,
|
||||
Callable,
|
||||
Dict,
|
||||
Iterable,
|
||||
List,
|
||||
Optional,
|
||||
@@ -85,49 +87,192 @@ class Scalar(Statistic):
|
||||
self.datatype = datatype
|
||||
|
||||
|
||||
class BaseScalarVector(Statistic):
|
||||
class Vector(Statistic):
|
||||
"""
|
||||
An abstract base class for classes containing a vector of Scalar values.
|
||||
An Python statistics which representing a vector of Scalar values.
|
||||
"""
|
||||
|
||||
value: List[Union[int, float]]
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
value: Iterable[Union[int, float]],
|
||||
value: Dict[Union[str, int, float], Scalar],
|
||||
type: Optional[str] = None,
|
||||
description: Optional[str] = None,
|
||||
):
|
||||
super().__init__(
|
||||
value=list(value),
|
||||
value=value,
|
||||
type=type,
|
||||
description=description,
|
||||
)
|
||||
|
||||
def __getitem__(self, item: Union[int, str, float]) -> Scalar:
|
||||
assert self.value != None
|
||||
# In the case of string, we cast strings to integers of floats if they
|
||||
# are numeric. This avoids users having to cast strings to integers.
|
||||
if isinstance(item, str):
|
||||
if item.isdigit():
|
||||
item = int(item)
|
||||
elif item.isnumeric():
|
||||
item = float(item)
|
||||
return self.value[item]
|
||||
|
||||
def __contains__(self, item) -> bool:
|
||||
assert self.value != None
|
||||
if isinstance(item, str):
|
||||
if item.isdigit():
|
||||
item = int(item)
|
||||
elif item.isnumeric():
|
||||
item = float(item)
|
||||
return item in self.value
|
||||
|
||||
def __iner__(self) -> None:
|
||||
return iter(self.value)
|
||||
|
||||
def __len__(self) -> int:
|
||||
assert self.value != None
|
||||
return len(self.value.values())
|
||||
|
||||
def size(self) -> int:
|
||||
"""
|
||||
Returns the size of the vector.
|
||||
|
||||
:returns: The size of the vector.
|
||||
"""
|
||||
assert self.value != None
|
||||
return len(self.value)
|
||||
|
||||
def mean(self) -> float:
|
||||
"""
|
||||
Returns the mean of the value vector.
|
||||
|
||||
:returns: The mean value across all bins.
|
||||
:returns: The mean value across all values in the vector.
|
||||
"""
|
||||
assert self.value != None
|
||||
assert isinstance(self.value, List)
|
||||
|
||||
from statistics import mean as statistics_mean
|
||||
|
||||
return statistics_mean(self.value)
|
||||
return self.count() / self.size()
|
||||
|
||||
def count(self) -> float:
|
||||
"""
|
||||
Returns the count across all the bins.
|
||||
Returns the count (sum) of all values in the vector.
|
||||
|
||||
:returns: The sum of all bin values.
|
||||
:returns: The sum of all vector values.
|
||||
"""
|
||||
assert self.value != None
|
||||
return sum(self.value)
|
||||
return sum(float(self.value[key]) for key in self.values)
|
||||
|
||||
def children(
|
||||
self,
|
||||
predicate: Optional[Callable[[str], bool]] = None,
|
||||
recursive: bool = False,
|
||||
) -> List["AbstractStat"]:
|
||||
to_return = []
|
||||
for attr in self.value.keys():
|
||||
obj = self.value[attr]
|
||||
if isinstance(obj, AbstractStat):
|
||||
if (
|
||||
isinstance(attr, str)
|
||||
and (predicate and predicate(attr))
|
||||
or not predicate
|
||||
):
|
||||
to_return.append(obj)
|
||||
to_return = to_return + obj.children(
|
||||
predicate=predicate, recursive=True
|
||||
)
|
||||
return to_return
|
||||
|
||||
|
||||
class Distribution(BaseScalarVector):
|
||||
class Vector2d(Statistic):
|
||||
"""
|
||||
A 2D vector of scalar values.
|
||||
"""
|
||||
|
||||
value: Dict[Union[str, int, float], Vector]
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
value: Dict[Union[str, int, float], Vector],
|
||||
type: Optional[str] = None,
|
||||
description: Optional[str] = None,
|
||||
):
|
||||
assert (
|
||||
len({vector.size() for vector in value.values()}) == 1
|
||||
), "All the Vectors in the 2d Vector are not of equal length."
|
||||
|
||||
super().__init__(
|
||||
value=value,
|
||||
type=type,
|
||||
description=description,
|
||||
)
|
||||
|
||||
def x_size(self) -> int:
|
||||
"""Returns the number of elements in the x dimension."""
|
||||
assert self.value is not None
|
||||
return len(self.value)
|
||||
|
||||
def y_size(self) -> int:
|
||||
"""Returns the number of elements in the y dimension."""
|
||||
assert self.value is not None
|
||||
return len(self.value[0])
|
||||
|
||||
def size(self) -> int:
|
||||
"""Returns the total number of elements."""
|
||||
return self.x_size() * self.y_size()
|
||||
|
||||
def __len__(self) -> int:
|
||||
return self.x_size()
|
||||
|
||||
def __iter__(self):
|
||||
return iter(self.keys())
|
||||
|
||||
def total(self) -> int:
|
||||
"""The total (sum) of all the entries in the 2d vector/"""
|
||||
assert self.value is not None
|
||||
total = 0
|
||||
for vector in self.value.values():
|
||||
for scalar in vector.values():
|
||||
total += scalar.value
|
||||
return total
|
||||
|
||||
def __getitem__(self, index: Union[str, int, float]) -> Vector:
|
||||
assert self.value is not None
|
||||
# In the case of string, we cast strings to integers of floats if they
|
||||
# are numeric. This avoids users having to cast strings to integers.
|
||||
if isinstance(index, str):
|
||||
if index.isindex():
|
||||
index = int(index)
|
||||
elif index.isnumeric():
|
||||
index = float(index)
|
||||
return self.value[index]
|
||||
|
||||
def children(
|
||||
self,
|
||||
predicate: Optional[Callable[[str], bool]] = None,
|
||||
recursive: bool = False,
|
||||
) -> List["AbstractStat"]:
|
||||
to_return = []
|
||||
for attr in self.value.keys():
|
||||
obj = self.value[attr]
|
||||
if (
|
||||
isinstance(attr, str)
|
||||
and (predicate and predicate(attr))
|
||||
or not predicate
|
||||
):
|
||||
to_return.append(obj)
|
||||
to_return = to_return + obj.children(
|
||||
predicate=predicate, recursive=True
|
||||
)
|
||||
return to_return
|
||||
|
||||
def __contains__(self, item) -> bool:
|
||||
assert self.value is not None
|
||||
if isinstance(item, str):
|
||||
if item.isdigit():
|
||||
item = int(item)
|
||||
elif item.isnumeric():
|
||||
item = float(item)
|
||||
return item in self.value
|
||||
|
||||
|
||||
class Distribution(Vector):
|
||||
"""
|
||||
A statistic type that stores information relating to distributions. Each
|
||||
distribution has a number of bins (>=1)
|
||||
@@ -149,7 +294,7 @@ class Distribution(BaseScalarVector):
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
value: Iterable[int],
|
||||
value: Dict[Union[int, float], Scalar],
|
||||
min: Union[float, int],
|
||||
max: Union[float, int],
|
||||
num_bins: int,
|
||||
@@ -182,35 +327,29 @@ class Distribution(BaseScalarVector):
|
||||
assert self.num_bins >= 1
|
||||
|
||||
|
||||
class Accumulator(BaseScalarVector):
|
||||
"""
|
||||
A statistical type representing an accumulator.
|
||||
"""
|
||||
|
||||
_count: int
|
||||
min: Union[int, float]
|
||||
max: Union[int, float]
|
||||
sum_squared: Optional[int]
|
||||
class SparseHist(Vector):
|
||||
"""A Sparse Histogram of values. A sparse histogram simply counts the "
|
||||
frequency of each value in a sample. Ergo, it is, ineffect an disctionary
|
||||
of values mapped to their count"""
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
value: Iterable[Union[int, float]],
|
||||
count: int,
|
||||
min: Union[int, float],
|
||||
max: Union[int, float],
|
||||
sum_squared: Optional[int] = None,
|
||||
value: Dict[float, Scalar],
|
||||
description: Optional[str] = None,
|
||||
):
|
||||
super().__init__(
|
||||
value=value,
|
||||
type="Accumulator",
|
||||
type="SparseHist",
|
||||
description=description,
|
||||
)
|
||||
|
||||
self._count = count
|
||||
self.min = min
|
||||
self.max = max
|
||||
self.sum_squared = sum_squared
|
||||
def size(self) -> int:
|
||||
"""The number of unique sampled values."""
|
||||
return len(self.value)
|
||||
|
||||
def count(self) -> int:
|
||||
return self._count
|
||||
"""
|
||||
Returns the total number of samples.
|
||||
"""
|
||||
assert self.value != None
|
||||
return sum(self.value.values())
|
||||
|
||||
@@ -41,6 +41,7 @@ from m5.ext.pystats.simstat import *
|
||||
from m5.ext.pystats.statistic import *
|
||||
from m5.ext.pystats.storagetype import *
|
||||
from m5.objects import *
|
||||
from m5.params import SimObjectVector
|
||||
|
||||
import _m5.stats
|
||||
|
||||
@@ -83,33 +84,6 @@ class JsonOutputVistor:
|
||||
simstat.dump(fp=fp, **self.json_args)
|
||||
|
||||
|
||||
def get_stats_group(group: _m5.stats.Group) -> Group:
|
||||
"""
|
||||
Translates a gem5 Group object into a Python stats Group object. A Python
|
||||
statistic Group object is a dictionary of labeled Statistic objects. Any
|
||||
gem5 object passed to this will have its ``getStats()`` and ``getStatGroups``
|
||||
function called, and all the stats translated (inclusive of the stats
|
||||
further down the hierarchy).
|
||||
|
||||
:param group: The gem5 _m5.stats.Group object to be translated to be a Python
|
||||
stats Group object. Typically this will be a gem5 SimObject.
|
||||
|
||||
:returns: The stats group object translated from the input gem5 object.
|
||||
"""
|
||||
|
||||
stats_dict = {}
|
||||
|
||||
for stat in group.getStats():
|
||||
statistic = __get_statistic(stat)
|
||||
if statistic is not None:
|
||||
stats_dict[stat.name] = statistic
|
||||
|
||||
for key in group.getStatGroups():
|
||||
stats_dict[key] = get_stats_group(group.getStatGroups()[key])
|
||||
|
||||
return Group(**stats_dict)
|
||||
|
||||
|
||||
def __get_statistic(statistic: _m5.stats.Info) -> Optional[Statistic]:
|
||||
"""
|
||||
Translates a _m5.stats.Info object into a Statistic object, to process
|
||||
@@ -138,6 +112,10 @@ def __get_statistic(statistic: _m5.stats.Info) -> Optional[Statistic]:
|
||||
pass
|
||||
elif isinstance(statistic, _m5.stats.VectorInfo):
|
||||
return __get_vector(statistic)
|
||||
elif isinstance(statistic, _m5.stats.Vector2dInfo):
|
||||
return __get_vector2d(statistic)
|
||||
elif isinstance(statistic, _m5.stats.SparseHistInfo):
|
||||
return __get_sparse_hist(statistic)
|
||||
|
||||
return None
|
||||
|
||||
@@ -167,8 +145,16 @@ def __get_distribution(statistic: _m5.stats.DistInfo) -> Distribution:
|
||||
overflow = statistic.overflow
|
||||
logs = statistic.logs
|
||||
|
||||
parsed_values = {}
|
||||
for index in range(len(value)):
|
||||
parsed_values[index] = Scalar(
|
||||
value=value[index],
|
||||
unit=statistic.unit,
|
||||
datatype=StorageType["f64"],
|
||||
)
|
||||
|
||||
return Distribution(
|
||||
value=value,
|
||||
value=parsed_values,
|
||||
min=min,
|
||||
max=max,
|
||||
num_bins=num_bins,
|
||||
@@ -183,29 +169,97 @@ def __get_distribution(statistic: _m5.stats.DistInfo) -> Distribution:
|
||||
|
||||
|
||||
def __get_vector(statistic: _m5.stats.VectorInfo) -> Vector:
|
||||
to_add = dict()
|
||||
vec: Dict[Union[str, int, float], Scalar] = {}
|
||||
|
||||
for index in range(statistic.size):
|
||||
# All the values in a Vector are Scalar values
|
||||
value = statistic.value[index]
|
||||
unit = statistic.unit
|
||||
description = statistic.subdescs[index]
|
||||
# ScalarInfo uses the C++ `double`.
|
||||
datatype = StorageType["f64"]
|
||||
assert isinstance(value, float) or isinstance(value, int)
|
||||
|
||||
# Sometimes elements within a vector are defined by their name. Other
|
||||
# times they have no name. When a name is not available, we name the
|
||||
# stat the index value.
|
||||
if str(statistic.subnames[index]):
|
||||
index_string = str(statistic.subnames[index])
|
||||
if len(statistic.subnames) > index and statistic.subnames[index]:
|
||||
index_subname = str(statistic.subnames[index])
|
||||
if index_subname.isdigit():
|
||||
index_subname = int(index_subname)
|
||||
elif index_subname.isnumeric():
|
||||
index_subname = float(index_subname)
|
||||
else:
|
||||
index_string = str(index)
|
||||
index_subname = index
|
||||
|
||||
to_add[index_string] = Scalar(
|
||||
value=value, unit=unit, description=description, datatype=datatype
|
||||
index_subdesc = None
|
||||
if len(statistic.subdescs) > index and statistic.subdescs[index]:
|
||||
index_subdesc = str(statistic.subdescs[index])
|
||||
else:
|
||||
index_subdesc = statistic.desc
|
||||
|
||||
vec[index_subname] = Scalar(
|
||||
value=value,
|
||||
unit=statistic.unit,
|
||||
description=index_subdesc,
|
||||
datatype=StorageType["f64"],
|
||||
)
|
||||
|
||||
return Vector(scalar_map=to_add)
|
||||
return Vector(
|
||||
vec,
|
||||
type="Vector",
|
||||
description=statistic.desc,
|
||||
)
|
||||
|
||||
|
||||
def __get_vector2d(statistic: _m5.stats.Vector2dInfo) -> Vector2d:
|
||||
# All the values in a 2D Vector are Scalar values
|
||||
description = statistic.desc
|
||||
x_size = statistic.x_size
|
||||
y_size = statistic.y_size
|
||||
|
||||
vector_rep: Dict[Union[str, int, float], Vector] = {}
|
||||
for x_index in range(x_size):
|
||||
x_index_string = x_index
|
||||
if x_index in statistic.subnames:
|
||||
x_index_string = str(statistic.subnames[x_index])
|
||||
|
||||
x_desc = description
|
||||
if x_index in statistic.subdescs:
|
||||
x_desc = str(statistic.subdescs[x_index])
|
||||
x_vec: Dict[str, Scalar] = {}
|
||||
for y_index in range(y_size):
|
||||
y_index_val = y_index
|
||||
if y_index in statistic.ysubnames:
|
||||
y_index_val = str(statistic.subnames[y_index])
|
||||
|
||||
x_vec[y_index_val] = Scalar(
|
||||
value=statistic.value[x_index * y_size + y_index],
|
||||
unit=statistic.unit,
|
||||
datatype=StorageType["f64"],
|
||||
)
|
||||
|
||||
vector_rep[x_index_string] = Vector(
|
||||
x_vec,
|
||||
type="Vector",
|
||||
description=x_desc,
|
||||
)
|
||||
|
||||
return Vector2d(value=vector_rep, type="Vector2d", description=description)
|
||||
|
||||
|
||||
def __get_sparse_hist(statistic: _m5.stats.SparseHistInfo) -> SparseHist:
|
||||
description = statistic.desc
|
||||
value = statistic.values
|
||||
|
||||
parsed_values = {}
|
||||
for val in value:
|
||||
parsed_values[val] = Scalar(
|
||||
value=value[val],
|
||||
unit=statistic.unit,
|
||||
datatype=StorageType["f64"],
|
||||
)
|
||||
|
||||
return SparseHist(
|
||||
value=parsed_values,
|
||||
description=description,
|
||||
)
|
||||
|
||||
|
||||
def _prepare_stats(group: _m5.stats.Group):
|
||||
@@ -222,8 +276,84 @@ def _prepare_stats(group: _m5.stats.Group):
|
||||
_prepare_stats(child)
|
||||
|
||||
|
||||
def _process_simobject_object(simobject: SimObject) -> SimObjectGroup:
|
||||
"""
|
||||
Processes the stats of a SimObject, and returns a dictionary of the stats
|
||||
for the SimObject with PyStats objects when appropriate.
|
||||
|
||||
:param simobject: The SimObject to process the stats for.
|
||||
|
||||
:returns: A dictionary of the PyStats stats for the SimObject.
|
||||
"""
|
||||
|
||||
assert isinstance(
|
||||
simobject, SimObject
|
||||
), "simobject param must be a SimObject."
|
||||
|
||||
stats = (
|
||||
{
|
||||
"name": simobject.get_name(),
|
||||
}
|
||||
if simobject.get_name()
|
||||
else {}
|
||||
)
|
||||
|
||||
for stat in simobject.getStats():
|
||||
val = __get_statistic(stat)
|
||||
if val:
|
||||
stats[stat.name] = val
|
||||
|
||||
for name, child in simobject._children.items():
|
||||
to_add = _process_simobject_stats(child)
|
||||
if to_add:
|
||||
stats[name] = to_add
|
||||
|
||||
for name, child in sorted(simobject.getStatGroups().items()):
|
||||
# Note: We are using the name of the group to determine if we have
|
||||
# already processed the group as a child simobject or a statistic.
|
||||
# This is to avoid SimObjectVector's being processed twice. It is far
|
||||
# from an ideal solution, but it works for now.
|
||||
if not any(
|
||||
re.compile(f"{to_match}" + r"\d*").search(name)
|
||||
for to_match in stats.keys()
|
||||
):
|
||||
stats[name] = Group(**_process_simobject_stats(child))
|
||||
|
||||
return SimObjectGroup(**stats)
|
||||
|
||||
|
||||
def _process_simobject_stats(
|
||||
simobject: Union[
|
||||
SimObject, SimObjectVector, List[Union[SimObject, SimObjectVector]]
|
||||
]
|
||||
) -> Union[List[Dict], Dict]:
|
||||
"""
|
||||
Processes the stats of a SimObject, SimObjectVector, or List of either, and
|
||||
returns a dictionary of the PySqtats for the SimObject.
|
||||
|
||||
:param simobject: The SimObject to process the stats for.
|
||||
|
||||
:returns: A dictionary of the stats for the SimObject.
|
||||
"""
|
||||
|
||||
if isinstance(simobject, SimObject):
|
||||
return _process_simobject_object(simobject)
|
||||
|
||||
if isinstance(simobject, Union[List, SimObjectVector]):
|
||||
stats_list = []
|
||||
for obj in simobject:
|
||||
stats_list.append(_process_simobject_stats(obj))
|
||||
return SimObjectVectorGroup(value=stats_list)
|
||||
|
||||
return {}
|
||||
|
||||
|
||||
def get_simstat(
|
||||
root: Union[SimObject, List[SimObject]], prepare_stats: bool = True
|
||||
root: Union[
|
||||
Union[SimObject, SimObjectVector],
|
||||
List[Union[SimObject, SimObjectVector]],
|
||||
],
|
||||
prepare_stats: bool = True,
|
||||
) -> SimStat:
|
||||
"""
|
||||
This function will return the SimStat object for a simulation given a
|
||||
@@ -243,7 +373,26 @@ def get_simstat(
|
||||
:Returns: The SimStat Object of the current simulation.
|
||||
|
||||
"""
|
||||
|
||||
if prepare_stats:
|
||||
_m5.stats.processDumpQueue()
|
||||
|
||||
stats_map = {}
|
||||
for r in root:
|
||||
if prepare_stats:
|
||||
if isinstance(r, list):
|
||||
for obj in r:
|
||||
_prepare_stats(obj)
|
||||
else:
|
||||
_prepare_stats(r)
|
||||
|
||||
stats = _process_simobject_stats(r).__dict__
|
||||
stats["name"] = r.get_name() if r.get_name() else "root"
|
||||
stats_map[stats["name"]] = stats
|
||||
|
||||
if len(stats_map) == 1:
|
||||
stats_map = stats_map[next(iter(stats_map))]
|
||||
|
||||
creation_time = datetime.now()
|
||||
time_converstion = None # TODO https://gem5.atlassian.net/browse/GEM5-846
|
||||
final_tick = Root.getInstance().resolveStat("finalTick").value
|
||||
@@ -251,31 +400,8 @@ def get_simstat(
|
||||
simulated_begin_time = int(final_tick - sim_ticks)
|
||||
simulated_end_time = int(final_tick)
|
||||
|
||||
if prepare_stats:
|
||||
_m5.stats.processDumpQueue()
|
||||
|
||||
for r in root:
|
||||
if isinstance(r, Root):
|
||||
# The Root is a special case, we jump directly into adding its
|
||||
# constituent Groups.
|
||||
if prepare_stats:
|
||||
_prepare_stats(r)
|
||||
for key in r.getStatGroups():
|
||||
stats_map[key] = get_stats_group(r.getStatGroups()[key])
|
||||
elif isinstance(r, SimObject):
|
||||
if prepare_stats:
|
||||
_prepare_stats(r)
|
||||
stats_map[r.get_name()] = get_stats_group(r)
|
||||
else:
|
||||
raise TypeError(
|
||||
"Object (" + str(r) + ") passed is not a "
|
||||
"SimObject. " + __name__ + " only processes "
|
||||
"SimObjects, or a list of SimObjects."
|
||||
)
|
||||
|
||||
return SimStat(
|
||||
creation_time=creation_time,
|
||||
time_conversion=time_converstion,
|
||||
simulated_begin_time=simulated_begin_time,
|
||||
simulated_end_time=simulated_end_time,
|
||||
**stats_map,
|
||||
|
||||
@@ -76,7 +76,9 @@ cast_stat_info(const statistics::Info *info)
|
||||
*/
|
||||
TRY_CAST(statistics::FormulaInfo);
|
||||
TRY_CAST(statistics::VectorInfo);
|
||||
TRY_CAST(statistics::Vector2dInfo);
|
||||
TRY_CAST(statistics::DistInfo);
|
||||
TRY_CAST(statistics::SparseHistInfo);
|
||||
|
||||
return py::cast(info);
|
||||
|
||||
@@ -183,6 +185,26 @@ pybind_init_stats(py::module_ &m_native)
|
||||
[](const statistics::VectorInfo &info) { return info.total(); })
|
||||
;
|
||||
|
||||
py::class_<statistics::Vector2dInfo, statistics::Info,
|
||||
std::unique_ptr<statistics::Vector2dInfo, py::nodelete>>(
|
||||
m, "Vector2dInfo")
|
||||
.def_readonly("x_size", &statistics::Vector2dInfo::x)
|
||||
.def_readonly("y_size", &statistics::Vector2dInfo::y)
|
||||
.def_readonly("subnames", &statistics::Vector2dInfo::subnames)
|
||||
.def_readonly("subdescs", &statistics::Vector2dInfo::subdescs)
|
||||
.def_readonly("ysubnames", &statistics::Vector2dInfo::y_subnames)
|
||||
.def_readonly("value", &statistics::Vector2dInfo::cvec)
|
||||
;
|
||||
|
||||
py::class_<statistics::SparseHistInfo, statistics::Info,
|
||||
std::unique_ptr<statistics::SparseHistInfo, py::nodelete>>(
|
||||
m, "SparseHistInfo")
|
||||
.def_property_readonly("values", //A Dict[float, int] of sample & count
|
||||
[](const statistics::SparseHistInfo &info) {
|
||||
return info.data.cmap;
|
||||
})
|
||||
;
|
||||
|
||||
py::class_<statistics::FormulaInfo, statistics::VectorInfo,
|
||||
std::unique_ptr<statistics::FormulaInfo, py::nodelete>>(
|
||||
m, "FormulaInfo")
|
||||
|
||||
@@ -27,5 +27,11 @@
|
||||
Import ("*")
|
||||
|
||||
if env['CONF']['USE_TEST_OBJECTS']:
|
||||
SimObject('StatTester.py', sim_objects=['StatTester', 'ScalarStatTester'])
|
||||
SimObject('StatTester.py', sim_objects=[
|
||||
'StatTester',
|
||||
'ScalarStatTester',
|
||||
'VectorStatTester',
|
||||
'Vector2dStatTester',
|
||||
'SparseHistStatTester',
|
||||
])
|
||||
Source('stat_tester.cc')
|
||||
|
||||
@@ -44,3 +44,57 @@ class ScalarStatTester(StatTester):
|
||||
cxx_class = "gem5::ScalarStatTester"
|
||||
|
||||
value = Param.Float("The scalar stat's value.")
|
||||
|
||||
|
||||
class VectorStatTester(StatTester):
|
||||
type = "VectorStatTester"
|
||||
cxx_header = "test_objects/stat_tester.hh"
|
||||
cxx_class = "gem5::VectorStatTester"
|
||||
|
||||
values = VectorParam.Float("The vector stat's values.")
|
||||
subnames = VectorParam.String(
|
||||
[],
|
||||
"The vector stat's subnames. If position is empty, index int is "
|
||||
"used instead.",
|
||||
)
|
||||
subdescs = VectorParam.String(
|
||||
[],
|
||||
"The vector stat's subdescriptions. If empty, the subdescriptions "
|
||||
"are not used.",
|
||||
)
|
||||
|
||||
|
||||
class Vector2dStatTester(StatTester):
|
||||
type = "Vector2dStatTester"
|
||||
cxx_header = "test_objects/stat_tester.hh"
|
||||
cxx_class = "gem5::Vector2dStatTester"
|
||||
|
||||
x_size = Param.Int("The number of elements in the x dimension.")
|
||||
y_size = Param.Int("The number of elements in the y dimension.")
|
||||
|
||||
values = VectorParam.Float("The vector stat's values, flattened.")
|
||||
subnames = VectorParam.String(
|
||||
[],
|
||||
"The vector stat's subnames. If position is empty, index int is "
|
||||
"used instead.",
|
||||
)
|
||||
subdescs = VectorParam.String(
|
||||
[],
|
||||
"The vector stat's subdescriptions. If empty, the subdescriptions "
|
||||
"are not used.",
|
||||
)
|
||||
ysubnames = VectorParam.String(
|
||||
[],
|
||||
"The vector stat's y subdescriptions. If empty, the subdescriptions ",
|
||||
)
|
||||
|
||||
|
||||
class SparseHistStatTester(StatTester):
|
||||
type = "SparseHistStatTester"
|
||||
cxx_header = "test_objects/stat_tester.hh"
|
||||
cxx_class = "gem5::SparseHistStatTester"
|
||||
|
||||
samples = VectorParam.Float(
|
||||
"The sparse histogram's sampled values, to be inserted into the "
|
||||
"histogram."
|
||||
)
|
||||
|
||||
@@ -28,6 +28,8 @@
|
||||
|
||||
#include "test_objects/stat_tester.hh"
|
||||
|
||||
#include <set>
|
||||
|
||||
#include "base/stats/group.hh"
|
||||
|
||||
namespace gem5
|
||||
@@ -51,4 +53,110 @@ ScalarStatTester::ScalarStatTesterStats::ScalarStatTesterStats(
|
||||
{
|
||||
}
|
||||
|
||||
void
|
||||
VectorStatTester::setStats()
|
||||
{
|
||||
for (int i = 0; i < params.values.size(); i++)
|
||||
{
|
||||
stats.vector[i] = (params.values[i]);
|
||||
}
|
||||
}
|
||||
|
||||
VectorStatTester::VectorStatTesterStats::VectorStatTesterStats(
|
||||
statistics::Group *parent,
|
||||
const VectorStatTesterParams ¶ms
|
||||
) : statistics::Group(parent),
|
||||
vector(this,
|
||||
params.name.c_str(),
|
||||
statistics::units::Count::get(),
|
||||
params.description.c_str()
|
||||
)
|
||||
{
|
||||
vector.init(params.values.size());
|
||||
for (int i = 0; i < params.values.size(); i++)
|
||||
{
|
||||
if (params.subnames.size() > i) {
|
||||
vector.subname(i, params.subnames[i]);
|
||||
} else {
|
||||
vector.subname(i, std::to_string(i));
|
||||
}
|
||||
if (params.subdescs.size() > i) {
|
||||
vector.subdesc(i, params.subdescs[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
Vector2dStatTester::setStats()
|
||||
{
|
||||
for (int i = 0; i < params.x_size; i++)
|
||||
{
|
||||
for (int j = 0; j < params.y_size; j++)
|
||||
{
|
||||
stats.vector2d[i][j] = (params.values[j + i * params.y_size]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Vector2dStatTester::Vector2dStatTesterStats::Vector2dStatTesterStats(
|
||||
statistics::Group *parent,
|
||||
const Vector2dStatTesterParams ¶ms
|
||||
) : statistics::Group(parent),
|
||||
vector2d(this,
|
||||
params.name.c_str(),
|
||||
statistics::units::Count::get(),
|
||||
params.description.c_str()
|
||||
)
|
||||
{
|
||||
vector2d.init(params.x_size, params.y_size);
|
||||
|
||||
assert(params.x_size * params.y_size == params.values.size());
|
||||
|
||||
for (int i = 0; i < params.x_size; i++)
|
||||
{
|
||||
if (params.subnames.size() > i) {
|
||||
vector2d.subname(i, params.subnames[i]);
|
||||
} else {
|
||||
vector2d.subname(i, std::to_string(i));
|
||||
}
|
||||
if (params.subdescs.size() > i) {
|
||||
vector2d.subdesc(i, params.subdescs[i]);
|
||||
}
|
||||
}
|
||||
for (int j = 0; j < params.y_size; j++)
|
||||
{
|
||||
if (params.ysubnames.size() > j) {
|
||||
vector2d.ysubname(j, params.ysubnames[j]);
|
||||
} else {
|
||||
vector2d.ysubname(j, std::to_string(j));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void
|
||||
SparseHistStatTester::setStats()
|
||||
{
|
||||
for (auto sample : params.samples) {
|
||||
stats.sparse_histogram.sample(sample);
|
||||
}
|
||||
}
|
||||
|
||||
SparseHistStatTester::SparseHistStatTesterStats::SparseHistStatTesterStats(
|
||||
statistics::Group *parent,
|
||||
const SparseHistStatTesterParams ¶ms
|
||||
) : statistics::Group(parent),
|
||||
sparse_histogram(
|
||||
this,
|
||||
params.name.c_str(),
|
||||
statistics::units::Count::get(),
|
||||
params.description.c_str()
|
||||
)
|
||||
{
|
||||
sparse_histogram.init(
|
||||
(std::set(params.samples.begin(), params.samples.end())).size()
|
||||
);
|
||||
}
|
||||
|
||||
} // namespace gem5
|
||||
|
||||
@@ -31,7 +31,10 @@
|
||||
|
||||
#include "base/statistics.hh"
|
||||
#include "params/ScalarStatTester.hh"
|
||||
#include "params/SparseHistStatTester.hh"
|
||||
#include "params/StatTester.hh"
|
||||
#include "params/Vector2dStatTester.hh"
|
||||
#include "params/VectorStatTester.hh"
|
||||
#include "sim/sim_object.hh"
|
||||
|
||||
namespace gem5
|
||||
@@ -114,6 +117,70 @@ class ScalarStatTester : public StatTester
|
||||
} stats;
|
||||
};
|
||||
|
||||
class VectorStatTester : public StatTester
|
||||
{
|
||||
private:
|
||||
VectorStatTesterParams params;
|
||||
|
||||
public:
|
||||
VectorStatTester(const VectorStatTesterParams &p) :
|
||||
StatTester(p), params(p), stats(this, p) {}
|
||||
|
||||
protected:
|
||||
void setStats() override;
|
||||
struct VectorStatTesterStats : public statistics::Group
|
||||
{
|
||||
VectorStatTesterStats(
|
||||
statistics::Group *parent,
|
||||
const VectorStatTesterParams ¶ms
|
||||
);
|
||||
statistics::Vector vector;
|
||||
} stats;
|
||||
};
|
||||
|
||||
class Vector2dStatTester : public StatTester
|
||||
{
|
||||
private:
|
||||
Vector2dStatTesterParams params;
|
||||
|
||||
public:
|
||||
Vector2dStatTester(const Vector2dStatTesterParams &p) :
|
||||
StatTester(p), params(p), stats(this, p) {}
|
||||
|
||||
protected:
|
||||
void setStats() override;
|
||||
struct Vector2dStatTesterStats : public statistics::Group
|
||||
{
|
||||
Vector2dStatTesterStats(
|
||||
statistics::Group *parent,
|
||||
const Vector2dStatTesterParams ¶ms
|
||||
);
|
||||
statistics::Vector2d vector2d;
|
||||
} stats;
|
||||
};
|
||||
|
||||
class SparseHistStatTester : public StatTester
|
||||
{
|
||||
private:
|
||||
SparseHistStatTesterParams params;
|
||||
|
||||
public:
|
||||
SparseHistStatTester(const SparseHistStatTesterParams &p) :
|
||||
StatTester(p), params(p), stats(this, p) {}
|
||||
|
||||
protected:
|
||||
void setStats() override;
|
||||
struct SparseHistStatTesterStats : public statistics::Group
|
||||
{
|
||||
SparseHistStatTesterStats(
|
||||
statistics::Group *parent,
|
||||
const SparseHistStatTesterParams ¶ms
|
||||
);
|
||||
statistics::SparseHistogram sparse_histogram;
|
||||
} stats;
|
||||
};
|
||||
|
||||
|
||||
} // namespace gem5
|
||||
|
||||
#endif // __STAT_TESTER_HH__
|
||||
|
||||
@@ -25,8 +25,8 @@
|
||||
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
import argparse
|
||||
import json
|
||||
import sys
|
||||
from typing import Union
|
||||
|
||||
import m5
|
||||
from m5.objects import (
|
||||
@@ -35,31 +35,25 @@ from m5.objects import (
|
||||
)
|
||||
from m5.stats.gem5stats import get_simstat
|
||||
|
||||
"""This script is used for checking that statistics set in the simulation are
|
||||
correctly parsed through to the python SimStats. This script sets the
|
||||
statistics using the "StatTester" simobjects and ensures verifies correctness
|
||||
against the expected JSON output and that produced by the SimStats module.
|
||||
"""This script is used for checking that Scaler statistics set in the simulation are
|
||||
correctly parsed through to the python Pystats.
|
||||
"""
|
||||
|
||||
parser = argparse.ArgumentParser(description="Tests the output of a SimStat.")
|
||||
subparsers = parser.add_subparsers(
|
||||
dest="statistic", help="SimStats statistic to test", required=True
|
||||
parser = argparse.ArgumentParser(
|
||||
description="Tests the output of a Scaler Pystat."
|
||||
)
|
||||
|
||||
scalar_parser = subparsers.add_parser(
|
||||
"scalar", help="Test a scalar statistic."
|
||||
)
|
||||
scalar_parser.add_argument(
|
||||
parser.add_argument(
|
||||
"value", type=float, help="The value of the scalar statistic."
|
||||
)
|
||||
scalar_parser.add_argument(
|
||||
parser.add_argument(
|
||||
"--name",
|
||||
type=str,
|
||||
default="scalar",
|
||||
required=False,
|
||||
help="The name of the scalar statistic.",
|
||||
)
|
||||
scalar_parser.add_argument(
|
||||
parser.add_argument(
|
||||
"--description",
|
||||
type=str,
|
||||
default="",
|
||||
@@ -69,27 +63,22 @@ scalar_parser.add_argument(
|
||||
|
||||
args = parser.parse_args()
|
||||
|
||||
expected_output = None
|
||||
stat_tester = None
|
||||
if args.statistic == "scalar":
|
||||
stat_tester = ScalarStatTester()
|
||||
stat_tester.name = args.name
|
||||
stat_tester.description = args.description
|
||||
stat_tester.value = args.value
|
||||
expected_output = {
|
||||
"type": "Group",
|
||||
"time_conversion": None,
|
||||
args.name: {
|
||||
"value": args.value,
|
||||
"type": "Scalar",
|
||||
"unit": "Count",
|
||||
"description": args.description,
|
||||
"datatype": "f64",
|
||||
},
|
||||
}
|
||||
|
||||
assert stat_tester is not None
|
||||
assert expected_output is not None
|
||||
stat_tester = ScalarStatTester()
|
||||
stat_tester.name = args.name
|
||||
stat_tester.description = args.description
|
||||
stat_tester.value = args.value
|
||||
expected_output = {
|
||||
"type": "SimObject",
|
||||
"name": "system",
|
||||
"time_conversion": None,
|
||||
args.name: {
|
||||
"value": args.value,
|
||||
"type": "Scalar",
|
||||
"unit": "Count",
|
||||
"description": args.description,
|
||||
"datatype": "f64",
|
||||
},
|
||||
}
|
||||
|
||||
root = Root(full_system=False, system=stat_tester)
|
||||
|
||||
@@ -97,14 +86,22 @@ m5.instantiate()
|
||||
m5.simulate()
|
||||
|
||||
simstats = get_simstat(stat_tester)
|
||||
output = simstats.to_json()["system"]
|
||||
output = simstats.to_json()
|
||||
|
||||
# Remove the time related fields from the outputs if they exist.
|
||||
# `creation_time` is not deterministic, and `simulated_begin_time` and
|
||||
# simulated_end_time are not under test here.
|
||||
for field in ["creation_time", "simulated_begin_time", "simulated_end_time"]:
|
||||
for map in [output, expected_output]:
|
||||
if field in map:
|
||||
del map[field]
|
||||
|
||||
if output != expected_output:
|
||||
print("Output statistics do not match expected:", file=sys.stderr)
|
||||
print("", file=sys.stderr)
|
||||
print("Expected:", file=sys.stderr)
|
||||
print(expected_output, file=sys.stderr)
|
||||
print(json.dumps(expected_output, indent=4), file=sys.stderr)
|
||||
print("", file=sys.stderr)
|
||||
print("Actual:", file=sys.stderr)
|
||||
print(output, file=sys.stderr)
|
||||
print(json.dumps(output, indent=4), file=sys.stderr)
|
||||
sys.exit(1)
|
||||
121
tests/gem5/stats/configs/pystat_sparse_dist_check.py
Normal file
121
tests/gem5/stats/configs/pystat_sparse_dist_check.py
Normal file
@@ -0,0 +1,121 @@
|
||||
# Copyright (c) 2024 The Regents of the University of California
|
||||
# All rights reserved.
|
||||
#
|
||||
# Redistribution and use in source and binary forms, with or without
|
||||
# modification, are permitted provided that the following conditions are
|
||||
# met: redistributions of source code must retain the above copyright
|
||||
# notice, this list of conditions and the following disclaimer;
|
||||
# redistributions in binary form must reproduce the above copyright
|
||||
# notice, this list of conditions and the following disclaimer in the
|
||||
# documentation and/or other materials provided with the distribution;
|
||||
# neither the name of the copyright holders nor the names of its
|
||||
# contributors may be used to endorse or promote products derived from
|
||||
# this software without specific prior written permission.
|
||||
#
|
||||
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
import argparse
|
||||
import json
|
||||
import sys
|
||||
|
||||
import m5
|
||||
from m5.objects import (
|
||||
Root,
|
||||
SparseHistStatTester,
|
||||
)
|
||||
from m5.stats.gem5stats import get_simstat
|
||||
|
||||
parser = argparse.ArgumentParser(
|
||||
description="Tests the output of a SparseHist Pystat."
|
||||
)
|
||||
parser.add_argument(
|
||||
"samples",
|
||||
help="delimited list representing the samples for the distributed "
|
||||
"histogram.",
|
||||
type=lambda s: [float(item) for item in s.split(",")],
|
||||
)
|
||||
|
||||
parser.add_argument(
|
||||
"--name",
|
||||
type=str,
|
||||
default="sparse_hist",
|
||||
required=False,
|
||||
help="The name of the Sparse Histogram statistic.",
|
||||
)
|
||||
|
||||
parser.add_argument(
|
||||
"--description",
|
||||
type=str,
|
||||
default=None,
|
||||
required=False,
|
||||
help="The description of the Sparse Histogram statistic.",
|
||||
)
|
||||
|
||||
args = parser.parse_args()
|
||||
|
||||
stat_tester = SparseHistStatTester(
|
||||
name=args.name, description=args.description, samples=args.samples
|
||||
)
|
||||
|
||||
root = Root(full_system=False, system=stat_tester)
|
||||
m5.instantiate()
|
||||
m5.simulate()
|
||||
|
||||
simstats = get_simstat(stat_tester)
|
||||
output = simstats.to_json()
|
||||
|
||||
|
||||
value_dict = {}
|
||||
for sample in args.samples:
|
||||
value_dict[sample] = (
|
||||
1 if sample not in value_dict else value_dict[sample] + 1
|
||||
)
|
||||
|
||||
scaler_dict = {}
|
||||
for key in value_dict:
|
||||
scaler_dict[key] = {
|
||||
"unit": "Count",
|
||||
"type": "Scalar",
|
||||
"description": None,
|
||||
"value": value_dict[key],
|
||||
"datatype": "f64",
|
||||
}
|
||||
|
||||
expected_output = {
|
||||
"type": "SimObject",
|
||||
"name": "system",
|
||||
"time_conversion": None,
|
||||
args.name: {
|
||||
"value": scaler_dict,
|
||||
"type": "SparseHist",
|
||||
"description": str(args.description),
|
||||
},
|
||||
}
|
||||
|
||||
# Remove the time related fields from the outputs if they exist.
|
||||
# `creation_time` is not deterministic, and `simulated_begin_time` and
|
||||
# simulated_end_time are not under test here.
|
||||
for field in ["creation_time", "simulated_begin_time", "simulated_end_time"]:
|
||||
for map in [output, expected_output]:
|
||||
if field in map:
|
||||
del map[field]
|
||||
|
||||
if output != expected_output:
|
||||
print("Output statistics do not match expected:", file=sys.stderr)
|
||||
print("", file=sys.stderr)
|
||||
print("Expected:", file=sys.stderr)
|
||||
print(json.dumps(expected_output, indent=4), file=sys.stderr)
|
||||
print("", file=sys.stderr)
|
||||
print("Actual:", file=sys.stderr)
|
||||
print(json.dumps(output, indent=4), file=sys.stderr)
|
||||
sys.exit(1)
|
||||
182
tests/gem5/stats/configs/pystat_vector2d_check.py
Normal file
182
tests/gem5/stats/configs/pystat_vector2d_check.py
Normal file
@@ -0,0 +1,182 @@
|
||||
# Copyright (c) 2024 The Regents of the University of California
|
||||
# All rights reserved.
|
||||
#
|
||||
# Redistribution and use in source and binary forms, with or without
|
||||
# modification, are permitted provided that the following conditions are
|
||||
# met: redistributions of source code must retain the above copyright
|
||||
# notice, this list of conditions and the following disclaimer;
|
||||
# redistributions in binary form must reproduce the above copyright
|
||||
# notice, this list of conditions and the following disclaimer in the
|
||||
# documentation and/or other materials provided with the distribution;
|
||||
# neither the name of the copyright holders nor the names of its
|
||||
# contributors may be used to endorse or promote products derived from
|
||||
# this software without specific prior written permission.
|
||||
#
|
||||
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
import argparse
|
||||
import json
|
||||
import sys
|
||||
|
||||
import m5
|
||||
from m5.objects import (
|
||||
Root,
|
||||
Vector2dStatTester,
|
||||
)
|
||||
from m5.stats.gem5stats import get_simstat
|
||||
|
||||
"""This script is used for checking that Vector2d statistics set in the
|
||||
simulation are correctly parsed through to the python Pystats.
|
||||
"""
|
||||
|
||||
parser = argparse.ArgumentParser(
|
||||
description="Tests the output of a Vector2D Pystat."
|
||||
)
|
||||
|
||||
parser.add_argument(
|
||||
"value",
|
||||
help="Comma delimited list representing the 2d vector in a flattened "
|
||||
"state.",
|
||||
type=lambda s: [float(item) for item in s.split(",")],
|
||||
)
|
||||
|
||||
parser.add_argument(
|
||||
"num_vectors",
|
||||
help="The number of vectors in the vector of vectors",
|
||||
type=int,
|
||||
)
|
||||
|
||||
parser.add_argument(
|
||||
"--name",
|
||||
type=str,
|
||||
default="vector2d",
|
||||
required=False,
|
||||
help="Name of the vector statistic.",
|
||||
)
|
||||
|
||||
parser.add_argument(
|
||||
"--description",
|
||||
type=str,
|
||||
default="",
|
||||
required=False,
|
||||
help="Description of the vector statistic.",
|
||||
)
|
||||
|
||||
parser.add_argument(
|
||||
"--subnames",
|
||||
help="delimited list representing the vector subnames",
|
||||
type=str,
|
||||
)
|
||||
|
||||
parser.add_argument(
|
||||
"--subdescs",
|
||||
help="delimited list representing the vector subdescs",
|
||||
type=str,
|
||||
)
|
||||
|
||||
parser.add_argument(
|
||||
"--ysubnames",
|
||||
help="delimited list representing the vector ysubnames",
|
||||
type=str,
|
||||
)
|
||||
|
||||
|
||||
args = parser.parse_args()
|
||||
|
||||
expected_output = None
|
||||
stat_tester = None
|
||||
|
||||
stat_tester = Vector2dStatTester()
|
||||
stat_tester.name = args.name
|
||||
stat_tester.description = args.description
|
||||
stat_tester.subnames = []
|
||||
if args.subnames:
|
||||
stat_tester.subnames = [str(item) for item in args.subnames.split(",")]
|
||||
|
||||
stat_tester.subdescs = []
|
||||
if args.subdescs:
|
||||
stat_tester.subdescs = [str(item) for item in args.subdescs.split(",")]
|
||||
|
||||
stat_tester.ysubnames = []
|
||||
if args.ysubnames:
|
||||
stat_tester.ysubnames = [str(item) for item in args.ysubnames.split(",")]
|
||||
|
||||
assert (
|
||||
len(args.value) % args.num_vectors == 0
|
||||
), "The number of values is not divisable by the number of vectors."
|
||||
|
||||
stat_tester.x_size = args.num_vectors
|
||||
stat_tester.y_size = len(args.value) / args.num_vectors
|
||||
stat_tester.values = args.value
|
||||
|
||||
vectors = {} # The representation we expect output.
|
||||
for x in range(args.num_vectors):
|
||||
x_index = x if x not in stat_tester.subnames else stat_tester.subnames[x]
|
||||
|
||||
vector = {}
|
||||
for y in range(stat_tester.y_size):
|
||||
to_add = args.value[
|
||||
int(y + (x * (len(args.value) / args.num_vectors)))
|
||||
]
|
||||
vector[y] = {
|
||||
"value": to_add,
|
||||
"type": "Scalar",
|
||||
"unit": "Count",
|
||||
"description": None,
|
||||
"datatype": "f64",
|
||||
}
|
||||
|
||||
vectors[x_index] = {
|
||||
"type": "Vector",
|
||||
"description": stat_tester.subdescs[x]
|
||||
if x in stat_tester.subdescs
|
||||
else stat_tester.description,
|
||||
"value": vector,
|
||||
}
|
||||
|
||||
expected_output = {
|
||||
"type": "SimObject",
|
||||
"name": "system",
|
||||
"time_conversion": None,
|
||||
args.name: {
|
||||
"type": "Vector2d",
|
||||
"value": vectors,
|
||||
"description": args.description,
|
||||
},
|
||||
}
|
||||
|
||||
root = Root(full_system=False, system=stat_tester)
|
||||
|
||||
m5.instantiate()
|
||||
m5.simulate()
|
||||
|
||||
simstats = get_simstat(stat_tester)
|
||||
output = simstats.to_json()
|
||||
|
||||
# Remove the time related fields from the outputs if they exist.
|
||||
# `creation_time` is not deterministic, and `simulated_begin_time` and
|
||||
# simulated_end_time are not under test here.
|
||||
for field in ["creation_time", "simulated_begin_time", "simulated_end_time"]:
|
||||
for map in [output, expected_output]:
|
||||
if field in map:
|
||||
del map[field]
|
||||
|
||||
if output != expected_output:
|
||||
print("Output statistics do not match expected:", file=sys.stderr)
|
||||
print("", file=sys.stderr)
|
||||
print("Expected:", file=sys.stderr)
|
||||
print(json.dumps(expected_output, indent=4), file=sys.stderr)
|
||||
print("", file=sys.stderr)
|
||||
print("Actual:", file=sys.stderr)
|
||||
print(json.dumps(output, indent=4), file=sys.stderr)
|
||||
sys.exit(1)
|
||||
147
tests/gem5/stats/configs/pystat_vector_check.py
Normal file
147
tests/gem5/stats/configs/pystat_vector_check.py
Normal file
@@ -0,0 +1,147 @@
|
||||
# Copyright (c) 2024 The Regents of the University of California
|
||||
# All rights reserved.
|
||||
#
|
||||
# Redistribution and use in source and binary forms, with or without
|
||||
# modification, are permitted provided that the following conditions are
|
||||
# met: redistributions of source code must retain the above copyright
|
||||
# notice, this list of conditions and the following disclaimer;
|
||||
# redistributions in binary form must reproduce the above copyright
|
||||
# notice, this list of conditions and the following disclaimer in the
|
||||
# documentation and/or other materials provided with the distribution;
|
||||
# neither the name of the copyright holders nor the names of its
|
||||
# contributors may be used to endorse or promote products derived from
|
||||
# this software without specific prior written permission.
|
||||
#
|
||||
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
import argparse
|
||||
import json
|
||||
import sys
|
||||
|
||||
import m5
|
||||
from m5.objects import (
|
||||
Root,
|
||||
VectorStatTester,
|
||||
)
|
||||
from m5.stats.gem5stats import get_simstat
|
||||
|
||||
"""This script is used for checking that the Vector statistics set in "
|
||||
the simulation are correctly parsed through to the python Pystats.
|
||||
"""
|
||||
|
||||
parser = argparse.ArgumentParser(
|
||||
description="Tests the output of a Vector PyStat."
|
||||
)
|
||||
|
||||
parser.add_argument(
|
||||
"value",
|
||||
help="Comma delimited list representing the vector.",
|
||||
type=lambda s: [float(item) for item in s.split(",")],
|
||||
)
|
||||
|
||||
parser.add_argument(
|
||||
"--name",
|
||||
type=str,
|
||||
default="vector",
|
||||
required=False,
|
||||
help="Name of the vector statistic.",
|
||||
)
|
||||
|
||||
parser.add_argument(
|
||||
"--description",
|
||||
type=str,
|
||||
default="",
|
||||
required=False,
|
||||
help="Description of the vector statistic.",
|
||||
)
|
||||
|
||||
parser.add_argument(
|
||||
"--subnames",
|
||||
help="Comma delimited list representing the vector subnames.",
|
||||
type=str,
|
||||
)
|
||||
|
||||
parser.add_argument(
|
||||
"--subdescs",
|
||||
help="Comma delimited list representing the vector subdescs",
|
||||
type=str,
|
||||
)
|
||||
|
||||
args = parser.parse_args()
|
||||
|
||||
stat_tester = VectorStatTester()
|
||||
stat_tester.name = args.name
|
||||
stat_tester.description = args.description
|
||||
stat_tester.values = args.value
|
||||
|
||||
stat_tester.subnames = []
|
||||
if args.subnames:
|
||||
stat_tester.subnames = [str(item) for item in args.subnames.split(",")]
|
||||
|
||||
stat_tester.subdescs = []
|
||||
if args.subdescs:
|
||||
stat_tester.subdescs = [str(item) for item in args.subdescs.split(",")]
|
||||
|
||||
value_dict = {}
|
||||
for i in range(len(args.value)):
|
||||
i_name = i
|
||||
description = args.description
|
||||
if stat_tester.subnames and i < len(stat_tester.subnames):
|
||||
i_name = stat_tester.subnames[i]
|
||||
if stat_tester.subdescs and i < len(stat_tester.subdescs):
|
||||
description = stat_tester.subdescs[i]
|
||||
|
||||
value_dict[i_name] = {
|
||||
"value": args.value[i],
|
||||
"type": "Scalar",
|
||||
"unit": "Count",
|
||||
"description": description,
|
||||
"datatype": "f64",
|
||||
}
|
||||
|
||||
expected_output = {
|
||||
"type": "SimObject",
|
||||
"name": "system",
|
||||
"time_conversion": None,
|
||||
args.name: {
|
||||
"value": value_dict,
|
||||
"type": "Vector",
|
||||
"description": args.description,
|
||||
},
|
||||
}
|
||||
|
||||
root = Root(full_system=False, system=stat_tester)
|
||||
|
||||
m5.instantiate()
|
||||
m5.simulate()
|
||||
|
||||
simstats = get_simstat(stat_tester)
|
||||
output = simstats.to_json()
|
||||
|
||||
# Remove the time related fields from the outputs if they exist.
|
||||
# `creation_time` is not deterministic, and `simulated_begin_time` and
|
||||
# simulated_end_time are not under test here.
|
||||
for field in ["creation_time", "simulated_begin_time", "simulated_end_time"]:
|
||||
for map in [output, expected_output]:
|
||||
if field in map:
|
||||
del map[field]
|
||||
|
||||
if output != expected_output:
|
||||
print("Output statistics do not match expected:", file=sys.stderr)
|
||||
print("", file=sys.stderr)
|
||||
print("Expected:", file=sys.stderr)
|
||||
print(json.dumps(expected_output, indent=4), file=sys.stderr)
|
||||
print("", file=sys.stderr)
|
||||
print("Actual:", file=sys.stderr)
|
||||
print(json.dumps(output, indent=4), file=sys.stderr)
|
||||
sys.exit(1)
|
||||
78
tests/gem5/stats/configs/pystats_simobjectvector_check.py
Normal file
78
tests/gem5/stats/configs/pystats_simobjectvector_check.py
Normal file
@@ -0,0 +1,78 @@
|
||||
# Copyright (c) 2024 The Regents of the University of California
|
||||
# All rights reserved.
|
||||
#
|
||||
# Redistribution and use in source and binary forms, with or without
|
||||
# modification, are permitted provided that the following conditions are
|
||||
# met: redistributions of source code must retain the above copyright
|
||||
# notice, this list of conditions and the following disclaimer;
|
||||
# redistributions in binary form must reproduce the above copyright
|
||||
# notice, this list of conditions and the following disclaimer in the
|
||||
# documentation and/or other materials provided with the distribution;
|
||||
# neither the name of the copyright holders nor the names of its
|
||||
# contributors may be used to endorse or promote products derived from
|
||||
# this software without specific prior written permission.
|
||||
#
|
||||
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
"""This script is used for checking that SimObject Vectorsare
|
||||
correctly parsed through to the gem5 PyStats."""
|
||||
|
||||
import m5
|
||||
from m5.objects import (
|
||||
Root,
|
||||
ScalarStatTester,
|
||||
VectorStatTester,
|
||||
)
|
||||
from m5.stats.gem5stats import get_simstat
|
||||
|
||||
root = Root(full_system=False)
|
||||
root.stat_testers = [
|
||||
ScalarStatTester(name="placeholder", value=11),
|
||||
ScalarStatTester(
|
||||
name="placeholder", value=22, description="Index 2 desc."
|
||||
),
|
||||
ScalarStatTester(name="placeholder", value=33),
|
||||
VectorStatTester(
|
||||
name="index_4",
|
||||
values=[44, 55, 66],
|
||||
description="A SimStat Vector within a SimObject Vector.",
|
||||
),
|
||||
]
|
||||
|
||||
m5.instantiate()
|
||||
m5.simulate()
|
||||
|
||||
simstat = get_simstat(root)
|
||||
|
||||
# 'stat_testers' is a list of SimObjects
|
||||
assert hasattr(simstat, "stat_testers"), "No stat_testers attribute found."
|
||||
assert len(simstat.stat_testers) == 4, "stat_testers list is not of length 3."
|
||||
|
||||
# Accessable by index.
|
||||
simobject = simstat.stat_testers[0]
|
||||
|
||||
# We can directly access the statistic we're interested in and its "str"
|
||||
# representation should be the same as the value we set. In this case "11.0".
|
||||
assert (
|
||||
str(simobject.placeholder) == "11.0"
|
||||
), "placeholder value is not 11.0 ()."
|
||||
|
||||
# They can also be accessed like so:
|
||||
# "other_stat" is a SimObject with a single stat called "stat".
|
||||
str(
|
||||
simstat["stat_testers"][3]["index_4"][0]
|
||||
) == "44.0", 'simstat[3]["index_4"][0] value is not 44.'
|
||||
|
||||
# We can also access other stats like type and description.
|
||||
assert simstat.stat_testers[1].placeholder.description == "Index 2 desc."
|
||||
assert simstat.stat_testers[1].placeholder.type == "Scalar"
|
||||
254
tests/gem5/stats/test_pystat_output.py
Normal file
254
tests/gem5/stats/test_pystat_output.py
Normal file
@@ -0,0 +1,254 @@
|
||||
# Copyright (c) 2024 The Regents of the University of California
|
||||
# All rights reserved.
|
||||
#
|
||||
# Redistribution and use in source and binary forms, with or without
|
||||
# modification, are permitted provided that the following conditions are
|
||||
# met: redistributions of source code must retain the above copyright
|
||||
# notice, this list of conditions and the following disclaimer;
|
||||
# redistributions in binary form must reproduce the above copyright
|
||||
# notice, this list of conditions and the following disclaimer in the
|
||||
# documentation and/or other materials provided with the distribution;
|
||||
# neither the name of the copyright holders nor the names of its
|
||||
# contributors may be used to endorse or promote products derived from
|
||||
# this software without specific prior written permission.
|
||||
#
|
||||
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
from testlib import *
|
||||
|
||||
gem5_verify_config(
|
||||
name="pystat-scaler-int-test",
|
||||
fixtures=(),
|
||||
verifiers=[],
|
||||
config=joinpath(
|
||||
config.base_dir,
|
||||
"tests",
|
||||
"gem5",
|
||||
"stats",
|
||||
"configs",
|
||||
"pystat_scalar_check.py",
|
||||
),
|
||||
config_args=[
|
||||
"42",
|
||||
"--name",
|
||||
"scalar_test",
|
||||
"--description",
|
||||
"A scalar statistic with a int value",
|
||||
],
|
||||
valid_isas=(constants.all_compiled_tag,),
|
||||
length=constants.quick_tag,
|
||||
)
|
||||
|
||||
gem5_verify_config(
|
||||
name="pystat-scaler-int-zero-test",
|
||||
fixtures=(),
|
||||
verifiers=[],
|
||||
config=joinpath(
|
||||
config.base_dir,
|
||||
"tests",
|
||||
"gem5",
|
||||
"stats",
|
||||
"configs",
|
||||
"pystat_scalar_check.py",
|
||||
),
|
||||
config_args=[
|
||||
"0",
|
||||
],
|
||||
valid_isas=(constants.all_compiled_tag,),
|
||||
length=constants.quick_tag,
|
||||
)
|
||||
|
||||
gem5_verify_config(
|
||||
name="pystat-scaler-int-negative-test",
|
||||
fixtures=(),
|
||||
verifiers=[],
|
||||
config=joinpath(
|
||||
config.base_dir,
|
||||
"tests",
|
||||
"gem5",
|
||||
"stats",
|
||||
"configs",
|
||||
"pystat_scalar_check.py",
|
||||
),
|
||||
config_args=[
|
||||
"-245",
|
||||
],
|
||||
valid_isas=(constants.all_compiled_tag,),
|
||||
length=constants.quick_tag,
|
||||
)
|
||||
|
||||
gem5_verify_config(
|
||||
name="pystat-scaler-float-test",
|
||||
fixtures=(),
|
||||
verifiers=[],
|
||||
config=joinpath(
|
||||
config.base_dir,
|
||||
"tests",
|
||||
"gem5",
|
||||
"stats",
|
||||
"configs",
|
||||
"pystat_scalar_check.py",
|
||||
),
|
||||
config_args=[
|
||||
"42.869",
|
||||
"--name",
|
||||
"float_test",
|
||||
"--description",
|
||||
"A scalar statistic with a float value",
|
||||
],
|
||||
valid_isas=(constants.all_compiled_tag,),
|
||||
length=constants.quick_tag,
|
||||
)
|
||||
|
||||
gem5_verify_config(
|
||||
name="pystat_vector_test",
|
||||
fixtures=(),
|
||||
verifiers=[],
|
||||
config=joinpath(
|
||||
config.base_dir,
|
||||
"tests",
|
||||
"gem5",
|
||||
"stats",
|
||||
"configs",
|
||||
"pystat_vector_check.py",
|
||||
),
|
||||
config_args=[
|
||||
"2.0,4,5.9,2.3,-8,0,0.0,-8.9",
|
||||
"--name",
|
||||
"vector_stat",
|
||||
"--description",
|
||||
"A vector statistic with a float value",
|
||||
],
|
||||
valid_isas=(constants.all_compiled_tag,),
|
||||
length=constants.quick_tag,
|
||||
)
|
||||
|
||||
gem5_verify_config(
|
||||
name="pystat_vector_with_subnames_test",
|
||||
fixtures=(),
|
||||
verifiers=[],
|
||||
config=joinpath(
|
||||
config.base_dir,
|
||||
"tests",
|
||||
"gem5",
|
||||
"stats",
|
||||
"configs",
|
||||
"pystat_vector_check.py",
|
||||
),
|
||||
config_args=[
|
||||
"2.0,4,3",
|
||||
"--name",
|
||||
"vector_stat",
|
||||
"--description",
|
||||
"A vector statistic with a float value",
|
||||
"--subnames",
|
||||
"first,second,third",
|
||||
],
|
||||
valid_isas=(constants.all_compiled_tag,),
|
||||
length=constants.quick_tag,
|
||||
)
|
||||
|
||||
gem5_verify_config(
|
||||
name="pystat_vector_with_subdescs_test",
|
||||
fixtures=(),
|
||||
verifiers=[],
|
||||
config=joinpath(
|
||||
config.base_dir,
|
||||
"tests",
|
||||
"gem5",
|
||||
"stats",
|
||||
"configs",
|
||||
"pystat_vector_check.py",
|
||||
),
|
||||
config_args=[
|
||||
"2.0,4,3",
|
||||
"--name",
|
||||
"vector_stat",
|
||||
"--description",
|
||||
"A vector statistic with a float value",
|
||||
"--subdescs",
|
||||
"first,second",
|
||||
],
|
||||
valid_isas=(constants.all_compiled_tag,),
|
||||
length=constants.quick_tag,
|
||||
)
|
||||
|
||||
gem5_verify_config(
|
||||
name="pystat_vector2d_test",
|
||||
fixtures=(),
|
||||
verifiers=[],
|
||||
config=joinpath(
|
||||
config.base_dir,
|
||||
"tests",
|
||||
"gem5",
|
||||
"stats",
|
||||
"configs",
|
||||
"pystat_vector2d_check.py",
|
||||
),
|
||||
config_args=[
|
||||
"2.4,4.3,3.7,-1.4,-2,4,0,0",
|
||||
2,
|
||||
"--name",
|
||||
"vector2d_stat",
|
||||
"--description",
|
||||
"A 2d vector statistic with",
|
||||
"--subnames",
|
||||
"decimals,integers",
|
||||
"--subdescs",
|
||||
"A random collection of decimals,A random collection of integers",
|
||||
"--ysubnames",
|
||||
"first,second,third,fourth",
|
||||
],
|
||||
valid_isas=(constants.all_compiled_tag,),
|
||||
length=constants.quick_tag,
|
||||
)
|
||||
|
||||
gem5_verify_config(
|
||||
name="pystat-sparsehist-test",
|
||||
fixtures=(),
|
||||
verifiers=[],
|
||||
config=joinpath(
|
||||
config.base_dir,
|
||||
"tests",
|
||||
"gem5",
|
||||
"stats",
|
||||
"configs",
|
||||
"pystat_sparse_dist_check.py",
|
||||
),
|
||||
config_args=[
|
||||
"1.0,1,1.00,23,23,0.2,0.2,0.2,0.2,-1,-1.0,264",
|
||||
"--name",
|
||||
"sparsehist_stat",
|
||||
"--description",
|
||||
"A sparse histogram statistic.",
|
||||
],
|
||||
valid_isas=(constants.all_compiled_tag,),
|
||||
length=constants.quick_tag,
|
||||
)
|
||||
|
||||
gem5_verify_config(
|
||||
name="simstat-simobjectvector-test",
|
||||
fixtures=(),
|
||||
verifiers=[],
|
||||
config=joinpath(
|
||||
config.base_dir,
|
||||
"tests",
|
||||
"gem5",
|
||||
"stats",
|
||||
"configs",
|
||||
"pystats_simobjectvector_check.py",
|
||||
),
|
||||
config_args=[],
|
||||
valid_isas=(constants.all_compiled_tag,),
|
||||
length=constants.quick_tag,
|
||||
)
|
||||
@@ -1,115 +0,0 @@
|
||||
# Copyright (c) 2024 The Regents of the University of California
|
||||
# All rights reserved.
|
||||
#
|
||||
# Redistribution and use in source and binary forms, with or without
|
||||
# modification, are permitted provided that the following conditions are
|
||||
# met: redistributions of source code must retain the above copyright
|
||||
# notice, this list of conditions and the following disclaimer;
|
||||
# redistributions in binary form must reproduce the above copyright
|
||||
# notice, this list of conditions and the following disclaimer in the
|
||||
# documentation and/or other materials provided with the distribution;
|
||||
# neither the name of the copyright holders nor the names of its
|
||||
# contributors may be used to endorse or promote products derived from
|
||||
# this software without specific prior written permission.
|
||||
#
|
||||
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
from testlib import *
|
||||
|
||||
gem5_verify_config(
|
||||
name="simstat-scaler-int-test",
|
||||
fixtures=(),
|
||||
verifiers=[],
|
||||
config=joinpath(
|
||||
config.base_dir,
|
||||
"tests",
|
||||
"gem5",
|
||||
"stats",
|
||||
"configs",
|
||||
"simstat_output_check.py",
|
||||
),
|
||||
config_args=[
|
||||
"scalar",
|
||||
"42",
|
||||
"--name",
|
||||
"scalar_test",
|
||||
"--description",
|
||||
"A scalar statistic with a int value",
|
||||
],
|
||||
valid_isas=(constants.all_compiled_tag,),
|
||||
length=constants.quick_tag,
|
||||
)
|
||||
|
||||
gem5_verify_config(
|
||||
name="simstat-scaler-int-zero-test",
|
||||
fixtures=(),
|
||||
verifiers=[],
|
||||
config=joinpath(
|
||||
config.base_dir,
|
||||
"tests",
|
||||
"gem5",
|
||||
"stats",
|
||||
"configs",
|
||||
"simstat_output_check.py",
|
||||
),
|
||||
config_args=[
|
||||
"scalar",
|
||||
"0",
|
||||
],
|
||||
valid_isas=(constants.all_compiled_tag,),
|
||||
length=constants.quick_tag,
|
||||
)
|
||||
|
||||
gem5_verify_config(
|
||||
name="simstat-scaler-int-negative-test",
|
||||
fixtures=(),
|
||||
verifiers=[],
|
||||
config=joinpath(
|
||||
config.base_dir,
|
||||
"tests",
|
||||
"gem5",
|
||||
"stats",
|
||||
"configs",
|
||||
"simstat_output_check.py",
|
||||
),
|
||||
config_args=[
|
||||
"scalar",
|
||||
"-245",
|
||||
],
|
||||
valid_isas=(constants.all_compiled_tag,),
|
||||
length=constants.quick_tag,
|
||||
)
|
||||
|
||||
gem5_verify_config(
|
||||
name="simstat-scaler-float-test",
|
||||
fixtures=(),
|
||||
verifiers=[],
|
||||
config=joinpath(
|
||||
config.base_dir,
|
||||
"tests",
|
||||
"gem5",
|
||||
"stats",
|
||||
"configs",
|
||||
"simstat_output_check.py",
|
||||
),
|
||||
config_args=[
|
||||
"scalar",
|
||||
"42.869",
|
||||
"--name",
|
||||
"float_test",
|
||||
"--description",
|
||||
"A scalar statistic with a float value",
|
||||
],
|
||||
valid_isas=(constants.all_compiled_tag,),
|
||||
length=constants.quick_tag,
|
||||
)
|
||||
208
tests/pyunit/pystats/pyunit_pystats.py
Normal file
208
tests/pyunit/pystats/pyunit_pystats.py
Normal file
@@ -0,0 +1,208 @@
|
||||
# Copyright (c) 2024 The Regents of The University of California
|
||||
# All rights reserved.
|
||||
#
|
||||
# Redistribution and use in source and binary forms, with or without
|
||||
# modification, are permitted provided that the following conditions are
|
||||
# met: redistributions of source code must retain the above copyright
|
||||
# notice, this list of conditions and the following disclaimer;
|
||||
# redistributions in binary form must reproduce the above copyright
|
||||
# notice, this list of conditions and the following disclaimer in the
|
||||
# documentation and/or other materials provided with the distribution;
|
||||
# neither the name of the copyright holders nor the names of its
|
||||
# contributors may be used to endorse or promote products derived from
|
||||
# this software without specific prior written permission.
|
||||
#
|
||||
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
import unittest
|
||||
from datetime import datetime
|
||||
|
||||
from m5.ext.pystats import (
|
||||
Distribution,
|
||||
Scalar,
|
||||
SimObjectGroup,
|
||||
SimObjectVectorGroup,
|
||||
SimStat,
|
||||
SparseHist,
|
||||
Vector,
|
||||
Vector2d,
|
||||
)
|
||||
|
||||
|
||||
def _get_mock_simstat() -> SimStat:
|
||||
"""Used to create a mock SimStat for testing.
|
||||
This SimStat is contains all simstat Statistic types and attempts to use
|
||||
most of the different types of values that can be stored in a Statistic.
|
||||
"""
|
||||
simobject_vector_group = SimObjectVectorGroup(
|
||||
value=[
|
||||
SimObjectGroup(
|
||||
**{
|
||||
"vector2d": Vector2d(
|
||||
value={
|
||||
0: Vector(
|
||||
value={
|
||||
"a": Scalar(value=1, description="one"),
|
||||
"b": Scalar(value=2.0, description="two"),
|
||||
"c": Scalar(value=-3, description="three"),
|
||||
}
|
||||
),
|
||||
1: Vector(
|
||||
value={
|
||||
1: Scalar(value=4),
|
||||
0.2: Scalar(value=5.0),
|
||||
0.3: Scalar(value=6),
|
||||
},
|
||||
description="vector 1",
|
||||
),
|
||||
},
|
||||
description="vector 2d",
|
||||
),
|
||||
}
|
||||
),
|
||||
SimObjectGroup(
|
||||
**{
|
||||
"distribution": Distribution(
|
||||
value={
|
||||
0: Scalar(1),
|
||||
1: Scalar(2),
|
||||
2: Scalar(3),
|
||||
3: Scalar(4),
|
||||
4: Scalar(5),
|
||||
},
|
||||
min=0,
|
||||
max=4,
|
||||
num_bins=5,
|
||||
bin_size=1,
|
||||
),
|
||||
"sparse_hist": SparseHist(
|
||||
value={
|
||||
0.5: Scalar(4),
|
||||
0.51: Scalar(1),
|
||||
0.511: Scalar(4),
|
||||
5: Scalar(2),
|
||||
},
|
||||
description="sparse hist",
|
||||
),
|
||||
},
|
||||
),
|
||||
],
|
||||
)
|
||||
|
||||
return SimStat(
|
||||
creation_time=datetime.fromisoformat("2021-01-01T00:00:00"),
|
||||
time_conversion=None,
|
||||
simulated_begin_time=123,
|
||||
simulated_end_time=558644,
|
||||
simobject_vector=simobject_vector_group,
|
||||
)
|
||||
|
||||
|
||||
class NavigatingPyStatsTestCase(unittest.TestCase):
|
||||
"""A test case for navigating the PyStats data structure, primarily
|
||||
on how to access children of a SimStat object, and the "find" methods to
|
||||
search for a specific statistic.
|
||||
"""
|
||||
|
||||
def setUp(self) -> None:
|
||||
"""Overrides the setUp method to create a mock SimStat for testing.
|
||||
Runs before each test method.
|
||||
"""
|
||||
self.failFast = True
|
||||
self.simstat = _get_mock_simstat()
|
||||
super().setUp()
|
||||
|
||||
def test_simstat_index(self):
|
||||
self.assertTrue("simobject_vector" in self.simstat)
|
||||
self.assertIsInstance(
|
||||
self.simstat["simobject_vector"], SimObjectVectorGroup
|
||||
)
|
||||
|
||||
def test_simstat_attribute(self):
|
||||
self.assertTrue(hasattr(self.simstat, "simobject_vector"))
|
||||
self.assertIsInstance(
|
||||
self.simstat.simobject_vector, SimObjectVectorGroup
|
||||
)
|
||||
|
||||
def test_simobject_vector_attribute(self):
|
||||
# To maintan compatibility with the old way of accessing the vector,
|
||||
# the simobject vectors values can be accessed by attributes of that
|
||||
# simoobject vector name and the index appended to it.
|
||||
# E.g., `simstat.simobject_vector0 is the same
|
||||
# is simstat.simobject_vector[0]`. In cases where there is already
|
||||
# an attribute with the same name as the vector+index, the attribute
|
||||
# will be returned.
|
||||
self.assertEqual(
|
||||
self.simstat.simobject_vector0, self.simstat.simobject_vector[0]
|
||||
)
|
||||
|
||||
def test_simobject_vector_index(self):
|
||||
self.assertTrue(self.simstat.simobject_vector[0], SimObjectGroup)
|
||||
|
||||
def test_simobject_group_index(self):
|
||||
self.assertTrue("vector2d" in self.simstat.simobject_vector[0])
|
||||
self.assertIsInstance(
|
||||
self.simstat.simobject_vector[0]["vector2d"], Vector2d
|
||||
)
|
||||
|
||||
def test_simobject_group_attribute(self):
|
||||
self.assertTrue(hasattr(self.simstat.simobject_vector[0], "vector2d"))
|
||||
self.assertIsInstance(
|
||||
self.simstat.simobject_vector[0].vector2d, Vector2d
|
||||
)
|
||||
|
||||
def test_vector2d_index(self):
|
||||
self.assertEqual(2, len(self.simstat.simobject_vector[0]["vector2d"]))
|
||||
self.assertTrue(0 in self.simstat.simobject_vector[0].vector2d)
|
||||
self.assertIsInstance(
|
||||
self.simstat.simobject_vector[0].vector2d[0], Vector
|
||||
)
|
||||
|
||||
def test_vector_index_int(self):
|
||||
self.assertEqual(3, len(self.simstat.simobject_vector[0].vector2d[1]))
|
||||
self.assertTrue(1 in self.simstat.simobject_vector[0].vector2d[1])
|
||||
self.assertIsInstance(
|
||||
self.simstat.simobject_vector[0].vector2d[1][1], Scalar
|
||||
)
|
||||
|
||||
def test_vector_index_str(self):
|
||||
self.assertEqual(3, len(self.simstat.simobject_vector[0].vector2d[0]))
|
||||
self.assertTrue("a" in self.simstat.simobject_vector[0].vector2d[0])
|
||||
self.assertIsInstance(
|
||||
self.simstat.simobject_vector[0].vector2d[0]["a"], Scalar
|
||||
)
|
||||
|
||||
def test_vector_index_float(self):
|
||||
self.assertEqual(3, len(self.simstat.simobject_vector[0].vector2d[1]))
|
||||
self.assertTrue(0.2 in self.simstat.simobject_vector[0].vector2d[1])
|
||||
self.assertIsInstance(
|
||||
self.simstat.simobject_vector[0].vector2d[1][0.2], Scalar
|
||||
)
|
||||
|
||||
def test_distriibution_index(self):
|
||||
self.assertTrue(0 in self.simstat.simobject_vector[1]["distribution"])
|
||||
self.assertIsInstance(
|
||||
self.simstat.simobject_vector[1]["distribution"][0], Scalar
|
||||
)
|
||||
|
||||
def test_sparse_hist_index(self):
|
||||
self.assertTrue(0.5 in self.simstat.simobject_vector[1]["sparse_hist"])
|
||||
self.assertIsInstance(
|
||||
self.simstat.simobject_vector[1]["sparse_hist"][0.5], Scalar
|
||||
)
|
||||
|
||||
def test_pystat_find(self):
|
||||
self.assertEqual(
|
||||
self.simstat.find("sparse_hist"),
|
||||
[self.simstat.simobject_vector[1]["sparse_hist"]],
|
||||
)
|
||||
Reference in New Issue
Block a user