From 6f15c98b9c25a85de3b81d49c05076606c5b6abc Mon Sep 17 00:00:00 2001 From: ilkilic <10600022+ilkilic@users.noreply.github.com> Date: Tue, 3 Sep 2024 16:25:07 +0200 Subject: [PATCH] perf: reduce cppcore initialisation (#410) --- CHANGELOG.rst | 5 +++++ efel/api.py | 8 ++++++-- efel/cppcore.pyi | 1 + efel/cppcore/cfeature.cpp | 7 +++++++ efel/cppcore/cfeature.h | 35 ++++++++++++++++++----------------- efel/cppcore/cppcore.cpp | 13 ++++++++++++- tests/test_cppcore.py | 15 +++++++++++++++ 7 files changed, 64 insertions(+), 20 deletions(-) diff --git a/CHANGELOG.rst b/CHANGELOG.rst index f4167729..29d54476 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -5,6 +5,11 @@ All notable changes to this project will be documented in this file. The format is based on `Keep a Changelog `_, and this project adheres to `Semantic Versioning `_. +5.7.9 - 2024-09 +--------------- + +- Reduces redundant dependency tree initializations in cppcore, improving feature extraction performance by up to 2.5x. Ensures getFeatureValues avoids unnecessary resets; use efel.reset to reload dependencies when needed. + 5.6.7 - 2024-08 --------------- diff --git a/efel/api.py b/efel/api.py index 738b633f..67e049eb 100644 --- a/efel/api.py +++ b/efel/api.py @@ -212,8 +212,11 @@ def _initialise() -> None: cppcore.Initialize(_settings.dependencyfile_path, "log") # flush the GErrorString from previous runs by calling getgError() cppcore.getgError() + _initSettings() - # Set the settings in the cppcore + +def _initSettings() -> None: + """Init the settings in cppcore.""" settings_attrs = vars(_settings) for setting_name, setting_value in settings_attrs.items(): if isinstance(setting_value, bool): @@ -356,7 +359,8 @@ def _get_feature_values_serial( else: raise Exception('stim_start or stim_end missing from trace') - _initialise() + cppcore.Clear() + _initSettings() # Next set time, voltage and the stimulus start and end for item in list(trace.keys()): diff --git a/efel/cppcore.pyi b/efel/cppcore.pyi index f64c8c4b..d0d53c74 100644 --- a/efel/cppcore.pyi +++ b/efel/cppcore.pyi @@ -1,4 +1,5 @@ def Initialize(depfilename: str, outfilename: str) -> int: ... +def Clear() -> int: ... def getFeature(feature_name: str, values: list) -> int: ... def getFeatureInt(feature_name: str, values: list[int]) -> int: ... def getFeatureDouble(feature_name: str, values: list[float]) -> int: ... diff --git a/efel/cppcore/cfeature.cpp b/efel/cppcore/cfeature.cpp index a2d0af0a..d7f7e3d0 100644 --- a/efel/cppcore/cfeature.cpp +++ b/efel/cppcore/cfeature.cpp @@ -54,6 +54,13 @@ cFeature::cFeature(const string& strDepFile, const string& outdir) logger << "Using dependency file: " << strDepFile << endl; } +void cFeature::clearMap() +{ + mapIntData.clear(); + mapDoubleData.clear(); + mapStrData.clear(); +} + template const vector cFeature::getMapData(const string& strName, const map>& mapData) { diff --git a/efel/cppcore/cfeature.h b/efel/cppcore/cfeature.h index 9739406f..d378bc4d 100644 --- a/efel/cppcore/cfeature.h +++ b/efel/cppcore/cfeature.h @@ -1,21 +1,21 @@ -/* Copyright (c) 2015, EPFL/Blue Brain Project - * - * This file is part of eFEL - * - * This library is free software; you can redistribute it and/or modify it under - * the terms of the GNU Lesser General Public License version 3.0 as published - * by the Free Software Foundation. - * - * This library is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS - * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more - * details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with this library; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +/* Copyright (c) 2015, EPFL/Blue Brain Project + * + * This file is part of eFEL + * + * This library is free software; you can redistribute it and/or modify it under + * the terms of the GNU Lesser General Public License version 3.0 as published + * by the Free Software Foundation. + * + * This library is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more + * details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this library; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ - + #ifndef CFEATURE_H_ #define CFEATURE_H_ @@ -62,6 +62,7 @@ class cFeature { string featuretype(string featurename); string getGError(); void get_feature_names(vector& feature_names); + void clearMap(); }; diff --git a/efel/cppcore/cppcore.cpp b/efel/cppcore/cppcore.cpp index 8f2fcb32..5ba1074b 100644 --- a/efel/cppcore/cppcore.cpp +++ b/efel/cppcore/cppcore.cpp @@ -54,6 +54,12 @@ int Initialize(const char* strDepFile, const char* outdir) { } } +void Clear() { + if (pFeature != NULL) { + pFeature->clearMap(); + } +} + static PyObject* CppCoreInitialize(PyObject* self, PyObject* args) { char *depfilename, *outfilename; if (!PyArg_ParseTuple(args, "ss", &depfilename, &outfilename)) { @@ -64,6 +70,11 @@ static PyObject* CppCoreInitialize(PyObject* self, PyObject* args) { return Py_BuildValue(""); } +static PyObject* CppCoreClear(PyObject* self, PyObject* args) { + Clear(); + return Py_BuildValue(""); +} + static vector PyList_to_vectorint(PyObject* input) { vector result_vector; int list_size; @@ -293,7 +304,7 @@ static PyObject* getgerrorstr(PyObject* self, PyObject* args) { static PyMethodDef CppCoreMethods[] = { {"Initialize", CppCoreInitialize, METH_VARARGS, "Initialise CppCore."}, - + {"Clear", CppCoreClear, METH_NOARGS, "Clear CppCore."}, {"getFeature", getfeature, METH_VARARGS, "Get a values associated with a feature. Takes a list() to be filled."}, {"getFeatureInt", getfeatureint, METH_VARARGS, "Get a integer feature."}, diff --git a/tests/test_cppcore.py b/tests/test_cppcore.py index 41711c71..1c1b531d 100644 --- a/tests/test_cppcore.py +++ b/tests/test_cppcore.py @@ -258,6 +258,21 @@ def test_caching(self, feature_name): # make sure Reusing computed value of text occurs twice assert contents.count(f"Reusing computed value of {feature_name}") == 2 + def test_clear_function(self): + """cppcore: Testing Clear function to reset state""" + import efel + self.setup_data() + + feature_values = list() + efel.cppcore.getFeature('AP_amplitude', feature_values) + assert len(feature_values) > 0 # Data should be present + + efel.cppcore.Clear() + + feature_values = list() + return_value = efel.cppcore.getFeature('AP_amplitude', feature_values) + assert return_value == -1 # Should return -1 since data is cleared + def test_efel_assertion_error(): """Testing if C++ assertion error is propagated to Python correctly."""