Move Caffe simulation to a separate thread.

Teapot now shows pretty much instantly after starting the program, and
then only reappears when switching inputs.
This commit is contained in:
2018-04-16 17:53:00 +02:00
parent 1fdb60f679
commit 51b537676d
5 changed files with 88 additions and 64 deletions

View File

@@ -6,6 +6,7 @@
#include "visualisations.hpp"
#include "Range.hpp"
#include "glutils.hpp"
#include "Simulator.hpp"
using namespace fmri;
@@ -228,25 +229,17 @@ void RenderingState::handleMouseAt(int x, int y)
glutPostRedisplay();
}
void RenderingState::loadSimulationData(const std::map<string, LayerInfo> &info, vector<vector<LayerData>> &&data)
{
layerInfo = std::move(info);
layerData = std::move(data);
currentData = layerData.begin();
queueUpdate();
}
void RenderingState::queueUpdate()
{
// Make sure that visualisations are cleared in the current thread
layerVisualisations.clear();
interactionAnimations.clear();
loadingFuture = std::async(std::launch::async, []() {
visualisationFuture = std::async(std::launch::async, []() {
RenderingState::instance().updateVisualisers();
return true;
});
isLoading = true;
}
void RenderingState::updateVisualisers()
@@ -278,7 +271,7 @@ void RenderingState::render(float time) const
// Clear Color and Depth Buffers
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
if (!isLoading) {
if (!isLoading()) {
renderVisualisation(time);
} else {
renderLoadingScreen();
@@ -418,6 +411,8 @@ void RenderingState::loadOptions(const Options &programOptions)
options.pathColor = programOptions.pathColor();
options.layerAlpha = programOptions.layerTransparancy();
options.interactionAlpha = programOptions.interactionTransparancy();
simulationFuture = std::async(std::launch::async, Simulator::loadSimulationData, programOptions);
}
const Color &RenderingState::pathColor() const
@@ -454,24 +449,47 @@ float RenderingState::layerAlpha() const
return options.layerAlpha;
}
/**
* Attempt to wait for completion of a future, for less than a frame.
*
* @tparam T
* @param f The future to wait for
* @return The result of the computation, or an empty optional if it hasn't finished.
*/
template<class T>
static std::optional<T> awaitCompletion(std::future<T>& f)
{
constexpr auto waitTime = std::chrono::milliseconds(10);
switch (f.wait_for(waitTime)) {
case std::future_status::timeout:
return std::nullopt;
case std::future_status::ready:
return f.get();
default:
LOG(ERROR) << "loading status was deferred, invalid state!";
abort();
}
}
void RenderingState::idleFunc()
{
if (isLoading && loadingFuture.valid()) {
auto result = loadingFuture.wait_for(std::chrono::milliseconds(16));
switch (result) {
case std::future_status::deferred:
LOG(ERROR) << "loading status was deferred, invalid state!";
abort();
case std::future_status::timeout:
// Still loading
break;
case std::future_status::ready:
loadingFuture.get();
if (isLoading()) {
if (visualisationFuture.valid()) {
auto result = awaitCompletion(visualisationFuture);
if (result) {
loadGLItems();
isLoading = false;
break;
return;
}
} else if (simulationFuture.valid()) {
auto result = awaitCompletion(simulationFuture);
if (result) {
tie(layerInfo, layerData) = std::move(*result);
currentData = layerData.begin();
queueUpdate();
}
}
} else {
if (options.mouse_1_pressed) {
@@ -490,3 +508,8 @@ void RenderingState::loadGLItems()
std::for_each(layerVisualisations.begin(), layerVisualisations.end(), [](auto& x) { x->glLoad(); });
std::for_each(interactionAnimations.begin(), interactionAnimations.end(), [](auto& x) { if (x) x->glLoad(); });
}
bool RenderingState::isLoading() const
{
return visualisationFuture.valid() || simulationFuture.valid();
}

View File

@@ -40,7 +40,6 @@ namespace fmri
void handleSpecialKey(int key);
void render(float time) const;
void loadSimulationData(const std::map<string, LayerInfo> &info, std::vector<std::vector<LayerData>> &&data);
/**
* Load rendering-specific options from the given options object.
*
@@ -79,8 +78,8 @@ namespace fmri
std::vector<std::vector<LayerData>>::iterator currentData;
std::vector<std::unique_ptr<LayerVisualisation>> layerVisualisations;
std::vector<std::unique_ptr<Animation>> interactionAnimations;
bool isLoading = false;
std::future<void> loadingFuture;
std::future<bool> visualisationFuture;
std::future<std::pair<decltype(layerInfo), decltype(layerData)>> simulationFuture;
RenderingState() noexcept;
@@ -102,5 +101,7 @@ namespace fmri
void renderVisualisation(float time) const;
void loadGLItems();
bool isLoading() const;
};
}

View File

@@ -231,3 +231,34 @@ const map<string, LayerInfo> & Simulator::layerInfo() const
{
return pImpl->layerInfo();
}
std::pair<std::map<std::string, LayerInfo>, std::vector<std::vector<LayerData>>>
fmri::Simulator::loadSimulationData(const Options &options)
{
Simulator simulator(options.model(), options.weights(), options.means());
std::vector<std::vector<LayerData>> results;
transform(options.inputs().begin(), options.inputs().end(), back_inserter(results), [&simulator] (auto& x) {
return simulator.simulate(x);
});
auto dumper = options.imageDumper();
if (dumper) {
for (auto &layer : *results.begin()) {
dumper->dump(layer);
}
}
const auto optLabels = options.labels();
if (optLabels) {
auto& labels = *optLabels;
for (const auto& result : results) {
auto &last = *result.rbegin();
auto bestIndex = std::distance(last.data(), max_element(last.data(), last.data() + last.numEntries()));
LOG(INFO) << "Got answer: " << labels[bestIndex] << endl;
}
}
return make_pair(simulator.layerInfo(), std::move(results));
}

View File

@@ -5,6 +5,7 @@
#include <vector>
#include "LayerData.hpp"
#include "LayerInfo.hpp"
#include "Options.hpp"
namespace fmri {
using std::string;
@@ -18,6 +19,8 @@ namespace fmri {
vector<LayerData> simulate(const string &input_file);
const std::map<std::string, LayerInfo>& layerInfo() const;
static std::pair<std::map<std::string, LayerInfo>, std::vector<std::vector<LayerData>>> loadSimulationData(const Options &options);
private:
struct Impl;
std::unique_ptr<Impl> pImpl;

View File

@@ -17,39 +17,6 @@
using namespace std;
using namespace fmri;
static void loadSimulationData(const Options &options)
{
vector<vector<LayerData>> results;
auto dumper = options.imageDumper();
Simulator simulator(options.model(), options.weights(), options.means());
for (const auto &image : options.inputs()) {
results.emplace_back(simulator.simulate(image));
}
CHECK_GT(results.size(), 0) << "Should have some results" << endl;
if (dumper) {
for (auto &layer : *results.begin()) {
dumper->dump(layer);
}
}
const auto optLabels = options.labels();
if (optLabels) {
auto& labels = *optLabels;
for (const auto& result : results) {
auto &last = *result.rbegin();
auto bestIndex = std::distance(last.data(), max_element(last.data(), last.data() + last.numEntries()));
LOG(INFO) << "Got answer: " << labels[bestIndex] << endl;
}
}
RenderingState::instance().loadSimulationData(simulator.layerInfo(), std::move(results));
}
int main(int argc, char *argv[])
{
google::InitGoogleLogging(argv[0]);
@@ -64,7 +31,6 @@ int main(int argc, char *argv[])
// Prepare data for simulations
Options options(argc, argv);
RenderingState::instance().loadOptions(options);
loadSimulationData(options);
// Register callbacks
glutReshapeFunc(changeWindowSize);