C++, multi-threading and queues

Discussion in 'Computer Science & Culture' started by AlphaNumeric, Oct 27, 2012.

Thread Status:
Not open for further replies.
  1. AlphaNumeric Fully ionized Moderator

    Messages:
    6,697
    So following all the fun with Java and then discovering someone wrote a ton of robot software known as MRPT in C++ I'm now looking at C++. Many concepts are common to C++ and Java which aren't in R or Mathematica so it's useful for work. As a motivating project I'm trying to make a nice GUI for a Kinect connected to my computer and then have commands piped through to an Arduino. I have the MRPT libraries for the Kinect. I have working serial communications with the Arduino (god damn I hate serial communication libraries!). I have vaguely understood the concept of a makefile and the way NetBeans handles different project types. The MRPT libraries include ways of calling multiple threads to deal with in and out commands. I'm trying to reproduce this basic "If I press anything then act on it, otherwise continue as normal" structure using multithreading, so I understand the principles.

    Presently it seems that all the guides I find online say I should use multithreading and queues as a producer-consumer setup. For example, listen_thread would listen for key input and add anything to a queue (using mutex) and other_thread is always doing something but checks the queue each iteration for any command to alter its behaviour.

    My question is whether this is the best way to go about it? I'm using pthread to do the threading, which is pretty straight forward, and the queing is simple via things like q.pop() etc. If down the line I want to have say 3 threads all producing and feeding into a single consumer does this approach scale? Should I be using something else if that's down the line?

    Calling multiple threads on a single PC means the PC architecture handles the load balancing, moving them around the multiple cores as it would any other set of programs. If a program makes extensive use of threading is there a simple way to not only spread them across cores but computers on a network or make use of GPUs? Or does that involve serious TCP/IP briding or some wacky GPU interface? I don't mean dynamic balancing, I might specific which computer does which thread. It isn't essential but it would be good to know.
     
  2. Google AdSense Guest Advertisement



    to hide all adverts.
  3. qsa Registered Member

    Messages:
    18
    It is not clear to me what your application does exactly that you would need such large numbers of threads. But my guess it would be easier and cheaper to scale to multi-CPU computers(check hp/dell ..) than multiple computers which is possible but expensive and the software is complicated. I gathered that you are familiar with GPU, they are good for parallelizing specific operations , like squaring a large array elements but not for different activities.


    http://h10010.www1.hp.com/wwpc/us/en/sm/WF04a/15351-15351-3328412-241644-4222584.html?dnr=1

    of course, you can scale to 32 CPUs(shared memory) and more but usually they are UNIX or other OS varieties.
     
  4. Google AdSense Guest Advertisement



    to hide all adverts.
  5. Crunchy Cat F-in' *meow* baby!!! Valued Senior Member

    Messages:
    8,423
    3 producers on their own threads feeding into a consumer queue is perfectly fine. Whether or not you should be using something different based on your future plans all depends on your future plans. If they don't exist yet then keep it simple and go with your current model.

    A simple way? Nope. A good and simplified way? Yes. There are TCP queuing servers out there that come with client APIs. Most are free and come with load balancing, multi-threaded prefetching, and online tutorials. Unfortunately C++ is a language that is often not supported by queuing vendors so what you get are community based APIs that work but can be difficult to use. On the other hand if MPRT supports Java then you would have a lot of excellent client APIs at your fingertips for queuing servers (I would recommend RabbitMq server by the way).

    p.s. I don't know what you are writing the GUI in, but if you are choosing C++ on a Windows system then your life is going to suck :3.
     
  6. Google AdSense Guest Advertisement



    to hide all adverts.
  7. Chipz Banned Banned

    Messages:
    838
    I'll write you a semaphore-based example some time tomorrow (basic example really only 50 loc or so).
    First I might mention, that doing a 'pop()' around an STL object is not thread-safe and won't work by itself. Additionally it "scales kind of". It really depends on the activity of each thread. The most obvious case of failure is the inputs of producers out perform the consumer leading to a queuing backlog and (inherently memory inflation caused) process crashing. Different input rates for producers may require different locking methods. Infrequent or unlikely thread locks may benefit most from a CAS lock, frequent and long collisions will benefit from a mutex or semaphore, and frequent short collisions may benefit from a spin lock.

    Crunchy Cat:
    AMPQ is written in C++, 0MQ is written in C. About half of messaging brokers are written in Java while the other half are in C based languages. I would be shocked if you could find any serious implementation without client bindings to either. Of the brokers some there are options to switch between UDP and TCP on nearly all implementations.
     
  8. Crunchy Cat F-in' *meow* baby!!! Valued Senior Member

    Messages:
    8,423
    Last I heard, AMQP is a protocol and not an actual product (whereas an implementation of AMQP would be Qpid for instance). One of the many clients of zeroMq is C and it is communally contributed to:

    https://github.com/zeromq/czmq

    However, it doesn't refute what I stated. I'll paraphrase for clarity. MQ server vendors often focus on writing client APIs in one or two languages (C++ not being a popular choice). The community then often kicks in and writes clients for other languages. What you often get is a couple of client APIs that are very easy to use and the rest are not so easy to use.

    Im not sure that's true (not that it's really relevant). Take two examples. RabbitMq is written in Erlang and Kafka is written in Scala. Either way, I don't think it's in the least helpful to AlphaNumeric.

    It's not a matter of server implementations without client APIs. It's a matter of server vendors not focusing their client API efforts on all the programming languages that exist (they would not have the resources to do so). And yes, I am sure there are solutions where you can use UDP, serial, IPX, or any number of options that probably wouldn't be good/relevant to AlphaNumeric's hypothetical question about distributed computing in the context of message queues.
     
    Last edited: Oct 31, 2012
  9. AlphaNumeric Fully ionized Moderator

    Messages:
    6,697
    It's partly just thinking about general coding and partly a specific example. There's a variety of tasks I might want the system to do; listen for user input, read Kinect data, process Kinect data for features, use features for 3d mapping, use features for object recognition, navigation planning, propulsion (ie motor) control, output various things to user. Obviously that's a LONG way down the road but I can imagine down the line in the sort of work I do having a good understanding of multi-threading, parallel computing, network load distribution and GPU utilisation will be very very useful.

    I know it's important to 'keep it simple stupid' but it's a good idea to get into good habits from the start, not acquaint myself with a terrible protocol and have to relearn everything 2 years down the line.

    Crunchy Cat, it's on an Ubuntu system, so not Windows.
     
  10. Crunchy Cat F-in' *meow* baby!!! Valued Senior Member

    Messages:
    8,423
    In that case you're on your own with the graphics as I know very little about Ubuntu

    Please Register or Log in to view the hidden image!

    . On a side note, I realize you want to practice good habits from the start (based on your response to qsa), but the way you expressed it makes me think that you might be blurring the line between good habits vs. choosing an appropriate architecture. Architecting software so it's flexible enough to handle everything and the kitchen sink necessarily comes at the cost of extreme complexity and mass quantities of extra work. It's actually better to write simple software and then incrementally refactor it as the needs of the software change. And don't worry, technology is moving so fast that in 2 years you are going to have to re-learn a bunch of stuff anyway.
     
  11. AlphaNumeric Fully ionized Moderator

    Messages:
    6,697
    Yeah, I realise there's a trade off between generality and practicality, I am currently aiming very much towards the latter but want to keep the former in mind. Plotting 3d stuff in C++ using this MRPT library seems pretty straight forward. My current approach is to take some example someone else wrote and then start deleting bits until I have stripped out all of the stuff unrelated to what I think I'm interested in and then try to write my own version (typically very poorly written and clunky but it works). I've managed to get my head around calling threads, USB communication, maths/vectors stuff and 3d plotting with that. Obviously not an indepth knowledge but enough to know how to do the specific examples. I'm about to the pain where C++ code isn't utter gibberish anymore, even if I still don't understand most of it (if you see what I mean).
     
  12. Crunchy Cat F-in' *meow* baby!!! Valued Senior Member

    Messages:
    8,423
    Considering you're a mathematician and not a software engineer, I think you are doing just fine. Most people can't even get their heads around basic pointers.
     
  13. AlphaNumeric Fully ionized Moderator

    Messages:
    6,697
    Got a different C++ question now. I'm reading sequences of char over a serial port, which correspond to a double. When I "cout <<" them as they come in then they look visually like decimal numbers, with the occasional \n to break them up. Using the \n I can automatically break the stream up into the individual 'numbers', sending the digits of a number into a std:vector<char>. When I "cout << v" I get the correct pieces, just spaced out (I assume that's just how vectors are displayed).

    How can I convert this vector (which in Mathematica notation would look like {1,2,3,4, . ,5}, equivalent to 1234.5 as a double) into the associated double? Using the method described here gives a double, just a completely wrong one.

    Thanks. I've now been messing around with Processing, the Java/Arduino-like language which is great for doing visual stuff, is more newbie friendly than Java or C++ and supports the skeleton tracking drivers for the Kinect. I got myself a book on how to do all kinds of stuff using Processing and Kinect, which I plan to do some mucking around with to get a grasp of the syntax more and also some of the geometric stuff I want to eventually be doing in C++. Surprisingly easy to knock up a program which tracks your movements and then displays a 3d scan of you, inserts 'objects' into this 3d rendering which then act as if you're really interacting with them, such as swinging a baseball bat or even bouncing a basketball around!

    I'm now getting into the phase where I can write rudimentary little bits of code without having to constantly Google for "C++, example, .... ". Still have no bloody idea about the liberal use of asterixes a lot of examples use but I'll get there...
     
  14. Chipz Banned Banned

    Messages:
    838
    This is a bit of a strange way to do things, but I'll take you at your word that it's necessary. Here's an example case of out to convert a vector of char into a double.

    Code:
    
    #include <iostream>
    #include <vector> 
    #include <sstream>  
    
    using namespace std;
    
    int
    main(int argc, char *argv[]) 
    {
            vector<char> v = { '1', '3', '2', '.', '8' };
            
            stringstream ss;
    
            for (size_t i = 0; i < v.size(); ++i) 
                    ss << v[i];
    
            double out;
            ss >> out;
    
            cerr << out << endl;
    
            return 0;
    }
    
     
  15. Crunchy Cat F-in' *meow* baby!!! Valued Senior Member

    Messages:
    8,423
    Hey CheskiChipz, I think AlphaNumeric *might* have meant that his vector of chars is a binary representation of double values (separated by newline chars).
     
  16. Crunchy Cat F-in' *meow* baby!!! Valued Senior Member

    Messages:
    8,423
    If the intended interpretation of the problem is that the vector contains sequences of chars that when smashed together look like a human-readable value then all you need to do is change the '\n' terminator to '\0' and then call the atof() function (which I believe is from #include <math.h>). A call to it would look like this:

    double myDbl = atof(&myVec[idx]); // Idx specifies where the next starting sequence of human-readable chars is.

    If the intended interpretation is that you are actually representing doubles as sequences of binary bytes (i.e. chars) then this is an example of how to convert from a double -> to a char vector -> back to a double:

    Code:
    // AlphaNTest.cpp : Defines the entry point for the console application.
    //
    
    #include "stdafx.h" // <--- This may be windows specific and you can remove it if it's not for you.
    #include <stdlib.h>
    #include <vector>
    
    class CConverters
    {
    public:
    	static void ToByteVec(double &srcDbl, 
    						  std::vector<char> &dstVec)
    	{
    		dstVec.resize(sizeof(srcDbl));
    		memcpy(&dstVec[0], &srcDbl, sizeof(srcDbl));
    	}
    
    	static void ToDbl(std::vector<char> &srcVec, 
    					  double &dstDbl) 
    	{ 
    		memcpy(&dstDbl, &srcVec[0], sizeof(dstDbl));
    	}
    }; 
    
    int _tmain(int argc, _TCHAR* argv[])
    {
    	double origDbl = 123456.789;	// Original double.
    	std::vector<char> byteVec;		// Dummy stream (vector of bytes).
    	double newDbl = 0;				// New double.
    
    	printf("Original double: %f\r\n", origDbl);
    	printf("Converting the original double to a byte vector...\r\n");
    	CConverters::ToByteVec(origDbl, byteVec);
    	printf("Converting the byte vector to a new double...\r\n");
    	CConverters::ToDbl(byteVec, newDbl);
    	printf("New double: %f\r\n\r\n", newDbl);
    
    	/*
    		OUTPUT:
    
    		Original double: 123456.789000
    		Converting the original double to a byte vector...
    		Converting the byte vector to a new double...
    		New double: 123456.789000
    	*/
    
    	system("pause"); // <--- This may be windows specific and you can remove it if it's not for you.
    	return 0;
    }
    
    That sounds really cool. Do you have any references to that Kinect stuff for tracking movements and displaying a 3d scan of a person? Yes, over time you will have lots of your own examples and won't have to hit google as often. Super amounts of asterisks can be confusing especially with double and triple indirection, but do it enough and it will become second nature :3.
     
  17. AlphaNumeric Fully ionized Moderator

    Messages:
    6,697
    No, it's the actual base 10 digits. To give some elaboration (some of which I've covered in previous threads in here) I have an Arduino (an open source microcontroller) which is sending things to the serial port (spoofed by the USB port obviously). Previously I've asked about how to sort out all the library issues for Java, which helped me work out how to do it in C++ too. So in my code I have 'SerialStream arduino', where SerialStream is a class defined by the relevant libraries. The Arduino can send any data it can generate but the form in which it does it is essentially to send text, which C++ must then be told how to interpret.

    My current method is to read the stream until it reads the line break '\n' command. The things read are stored in a vector of characters as follows

    Code:
                    char input_char = 'q'; // make single character entity
                    std::vector< char > input_buffer; // initialise vector
                    while(input_char!='\n'){ // repeat until you see line break
                        arduino.get(input_char); // read oldest piece of data from the SerialStream arduino
                        input_buffer.push_back(input_char); // add to vector
                    }      
    
    This will give me a vector with entries such as 1,2,3,4, . , 5,6 if the Arduino has sent 1234.56. I want to have a double X whose value is 1234.56. Chipz's method works but so does 'double value = atof(input_buffer.data());', the 'array to float' command. Now I can use the numbers I receive to do something rudimentary like plot a graph or control the motion of some point in some 3d plot.

    I wouldn't be in the slight bit surprised if this procedure and approach makes competent C++ coders bite pencils in half with disgust and frustration but I do elegant maths, I code like an elephant after 3 kilos of Valium.

    Thanks

    Please Register or Log in to view the hidden image!



    /edit

    CC, looks like you were posting the same time as me and suggested the atof method too

    Please Register or Log in to view the hidden image!

    Thanks
     
  18. Crunchy Cat F-in' *meow* baby!!! Valued Senior Member

    Messages:
    8,423
    Lol, timing is everything. atof() is a very clean solution for converting a string representation of a double to an actual double. Also, don't worry about writing strings to a serial port. There are entire enterprises who send all of their critical information over the internet in string format (XML). It simply has pros and cons like any approach and if any computer scientist-ish person tells you to never do it then you have my permission to give them a boot to the head. http://www.youtube.com/watch?v=9g1Z3V0QBpg
     
  19. AlphaNumeric Fully ionized Moderator

    Messages:
    6,697
    Part of learning is doing something the stupid or inefficient way and then coming across a slicker way to do it. Plus I still think in the procedural manner I got from using R and Mathematica. It's taking a while to got into the object orientated stuff and how you can pass data around pieces of programs. In R I often 'debug' by just printing everything to screen, running a small section of code and visually inspecting the results, which Java and C++ doesn't let you do. It's a bit like when I changed from Windows to Linux. I spent the first few weeks yelling at the PC "Just ****ing install! Where's the 'install' button! Why do I have to **** around with this apt-get crap! The wifi dongle is plugged in! Why doesn't Linux recognise it like Windows can!! ". Now whenever I use Windows I feel like I've got one hand tied behind my back from lack of low level control.

    I'm never going to be a slick programmer but I want to be competent enough to be able to sit down and knock out a crude implementation of some algorithm I am developing. Mathematica's great for instant graphics and symbolic stuff, while R is good for stats stuff but more and more I'm hitting their limitations when I want to do something efficient. I've got a CUDA compatible graphics card and need to run this serial stuff over a network but I think coding up things which parallelize over networks via UDP and exploit GPUs is better left for the future!
     
  20. Chipz Banned Banned

    Messages:
    838
    Boy-o-boy, I'm so happy to not be a Windows developer.

    Edit:
    And if anyone unfamiliar is reading... never never never never never never do these....

    Code:
    		memcpy(&dstVec[0], &srcDbl, sizeof(srcDbl));
    ...
    		memcpy(&dstDbl, &srcVec[0], sizeof(dstDbl));
    
    If someone submitted this to me for introduction into a production environment, I would be upset.
     
    Last edited: Nov 12, 2012
  21. Crunchy Cat F-in' *meow* baby!!! Valued Senior Member

    Messages:
    8,423
    If you think Windows is bad, try assembly on VAX. Win32 will seem like a walk in the park.

    I am assuming you are objecting to the memcpy because it is "unsafe". If that assumption is correct then I would tell anyone reading your "never never never..." advice to completely and utterly wipe it from their minds. "Safe" code in an unmanaged environment is a workaround used for incompetence. If someone knows what they are doing then unsafe code is preferred... always.
     
  22. Crunchy Cat F-in' *meow* baby!!! Valued Senior Member

    Messages:
    8,423
    There is more truth to that assertion than you may realize (especially in engineering).

    The most successful applications in the software industry right now tend to use both procedural and object oriented methodology. Both have pros and cons. Some IDEs for C++ and Java actually give you the ability to "script" snippets of code in real time and then execute them (even in the context of a running application). Visual Studio 2010 for C++ and IntelliJ for Java would be good examples of that.

    Hahaha, I hear that.

    I think you are on the right track :3.
     
  23. Chipz Banned Banned

    Messages:
    838
    There are numerous reasons not to do it. memcpy is an ANSI C-89 function, what on earth would make it unsafe? Believe it or not there is no concept of "unmanaged" or "managed" in C++, only in the Windows world of CLI extensions. Additionally your line "unsafe code is preferred... always" is absolute insanity.

    Code:
    float value(float *ptr, int len) {
            float  ret = 0.0;
            
            for ( ; len != 0; len--) {
                    ret += *(ptr++);
            }
            return ret;
    }
    
    This code is relatively unsafe compared do the standard for loop. Is it better?

    1. Breaking encapsulation
    Do you know that you're exposing managed memory would the void* ptr return of the memcpy function? When I did a comparison std::copy vs memcpy was about a 3:2 speed ratio which was admittedly unscientific, but why break encapsulation for such little gains?

    2. C++ is not C and C is not C++
    If you can get away with using a std::vector, you don't need C memory functions. In fact, that's the entire point of a vector.

    In this case either:
    a -- You will eventually have to pass this variable by value calling copy-constructor's (pre-C++11 move-constructors) which has to do a memcpy anyways.
    b -- Your memory is encapsulated in a traceable fashion such that the container would know to free it.
    c -- It's a member.

    In none of these cases is it sensible to execute non-standard functions on a container in a way which has undefined behaviour. You either need to use raw pointers and C memory access, or you don't need either.

    3. Do you really know why this works?
    The only reason this works at all is vector<char>[0] returns a reference to the encapsulated memory segment, you're acquiring the memory address of the reference of a managed memory segment and modifying it. Who knows what other encapsulated state variables are affected? Do you know for sure? Do you know if that will always be the case?
     
Thread Status:
Not open for further replies.

Share This Page