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."""