### Background computation threads with igl::viewer::Viewer

Sunday, December 4th, 2016

Here’s a minimal example showing how to launch background computation threads for each mesh in a list of meshes. Meanwhile the main thread runs a mesh viewer with all meshes concatenated into one huge multi-component mesh. Whenever a computation thread signals that an update to the mesh needs to be made, the main thread will re-concatenate the meshes and update the viewer. In this example, the “computation” is determining how much to move a clock “hand” (random mesh).

Here’s the program running on the cow, cheburashka and knight models:

// Tiny example to demonstrate spawning a background computation thread for
// each mesh in a list and update the viewer when computation results in a
// change to (one of) the meshes.
//
// In this example, three meshes are read in and interpreted as "hands" of a
// clock. The "computation" is just a busy-wait until the hand should move
// (after one second, one minute, one hour). This is, of course, a terrible way
// to implement a clock.
//
// ./test libigl/tutorial/shared/{cow.off,cheburashka.off,decimated-knight.off}
#include <igl/point_mesh_squared_distance.h>
#include <igl/combine.h>
#include <igl/viewer/Viewer.h>
#include <Eigen/Geometry>
int main(int argc, char * argv[])
{
using namespace std;
using namespace Eigen;
// Read in k meshes (<=3 will be used)
std::vector<Eigen::MatrixXd> VV(argc-1);
std::vector<Eigen::MatrixXi> FF(argc-1);
for(int i = 1;i<argc;i++)
{
VV[i-1].col(0).array() -= VV[i-1].col(0).mean();
VV[i-1].col(1).array() -= VV[i-1].col(1).minCoeff();
}
bool continue_computing = true;
// Flag to redraw and mutex to guard it
bool redraw = false;
std::mutex m;
// After waiting tic seconds, rotate V by deg degrees
const auto rot = [&continue_computing,&redraw,&m](
const double tic, const double deg, Eigen::MatrixXd& V)
{
while(continue_computing)
{
// Let's watch this at 500x: Real clocks are slow.
V *= Eigen::AngleAxisd(deg/180.*igl::PI,
Eigen::Vector3d(0,0,1)).toRotationMatrix();
{
std::lock_guard<std::mutex> lock(m);
redraw = true;
}
}
};
// Launch background "computation" threads for each "hand" of the clock
// std::ref is important, otherwise std::bind passes by copy
switch(VV.size())
{
case 3:
case 2:
case 1:
default: break;
}
igl::viewer::Viewer v;
v.data.clear();
// Helper function to view k meshes
const auto & set_meshes = [](
const std::vector<Eigen::MatrixXd> & VV,
const std::vector<Eigen::MatrixXi> & FF,
igl::viewer::Viewer & v)
{
Eigen::MatrixXd V;
Eigen::MatrixXi F;
igl::combine(VV,FF,V,F);
v.data.set_mesh(V,F);
};
set_meshes(VV,FF,v);
// Continuous draw loop. TODO: trigger draws using glfwPostEmptyEvent
v.core.is_animating = true;
// Before every draw check if the meshes have changed.
v.callback_pre_draw =
[&redraw,&m,&VV,&FF,&set_meshes](igl::viewer::Viewer & v)->bool
{
if(redraw)
{
set_meshes(VV,FF,v);
{
std::lock_guard<std::mutex> lock(m);
redraw = false;
}
}
return false;
};
v.launch();
// Tell computation threads to stop
continue_computing = false;
for(auto & t : threads) if(t.joinable()) t.join();
}

This is pretty hacky, but it will allow me to use the standard libigl viewer for making comparisons of mesh-editing methods whose performances are very different.

### Simple “parallel for” using C++11 std::thread

Thursday, February 25th, 2016

It seems like Apple’s built-in clang will never support OpenMP. I really used OpenMP only for its #omp parallel for. Now that C++11 has support for multi-threading, I tried to write a very basic parallel for without OpenMP.

#include <vector>
#include <iostream>

int main(int argc, char *argv[])
{
const size_t nloop = 11;

// Serial version
{
// Pre loop
std::cout<<"serial:"<<std::endl;
// loop over all items
for(int i = 0;i<nloop;i++)
{
// inner loop
{
const int j = i*i;
std::cout<<j<<std::endl;
}
}
// Post loop
std::cout<<std::endl;
}

// Parallel version
{
// Pre loop
std::mutex critical;
{
[&](const int bi, const int ei, const int t)
{
// loop over all items
for(int i = bi;i<ei;i++)
{
// inner loop
{
const int j = i*i;
// (optional) make output critical
std::lock_guard<std::mutex> lock(critical);
std::cout<<j<<std::endl;
}
}
}
// Post loop
std::cout<<std::endl;
}
}

It’s not as simple as slapping down #omp parallel for but it’s really just a few lines above and below the for loop. It can even determine the number of cores available and handle simple atomic operations.

### Bash script to find best number of threads for make’s -j option

Friday, November 16th, 2012

The make command lets you specify a number of processes to run commands on using the -j option. I’ve read of a heuristic to use 1.5 times the number of cores on your machine. To find out if this is true I wrote a short script:

#!/bin/bash

for i in {1..20}
do
make clean &>/dev/null
printf "$i " t=(time make -j$i &>/dev/null) 2>&1 | grep real | sed -e "s/real[^0-9]*//g"
echo "\$t"
done

For my project on my iMac with an intel i7 this prints:

1  1m1.235s
2  0m32.128s
3  0m23.353s
4  0m19.989s
5  0m18.007s
6  0m16.613s
7  0m15.490s
8  0m15.644s
9  0m16.307s
10  0m16.415s
11  0m16.861s
12  0m17.535s
13  0m17.053s
14  0m17.112s
15  0m17.550s
16  0m17.267s
17  0m17.274s
18  0m17.618s
19  0m17.540s
20  0m17.653s

Obviously you could improve the accuracy of these timing by running multiple times and averaging.

Tuesday, March 27th, 2012

Here’s a proof of concept that you can run in your matlab IDE that loads a workspace from a file in a background, worker thread. I imagine this is useful if you want to keep your Matlab session open, but have a 3rd party program be able to talk to matlab, (not just be called from matlab (mex) or call matlab functions (matlab engine).

First create some dummy workspaces.

X = 1;
save('workspace.mat','X');
X = 2;
save('workspace2.mat','X');

Now run this code to start the background “thread”:

% load the global workspace from 'workspace.mat' every 1 second, print value of X
start(t);

You should see that matlab is spitting out:

X =
1

But in the meantime you could issue matlab calls like:

Y = 'banana'

Now pretend you’re a third party program that wants to send a new value of X to the matlab session. Do this by copying our dummy workspace2.mat onto the workspace.mat that’s being polled:

!cp workspace2.mat workspace.mat

Now we see that it has worked as matlab is spitting out:

X =
2

Finally stop the polling and clean up the timer:

stop(t);
delete(t);