<p>If you were writing a new application from scratch today, and wanted it to scale to all the cores you could throw at it tomorrow, what parallel programming model/system/language/library would you choose?  Why?</p>

<p>I am particularly interested in answers along these axes:</p>

<ol>
<li>Programmer productivity / ease of use (can mortals successfully use it?)</li>
<li>Target application domain (what problems is it (not) good at?)</li>
<li>Concurrency style (does it support tasks, pipelines, data parallelism, messages...?)
<li>Maintainability / future-proofing (will anybody still be using it in 20 years?)</li>
<li>Performance (how does it scale on what kinds of hardware?)</li>
</ol>

<p>I am being deliberately vague on the nature of the application in anticipation of getting good general answers useful for a variety of applications.</p>

[{'answer_id': 80024, 'author': 'aku', 'author_id': 1196, 'author_profile': 'https://Stackoverflow.com/users/1196', 'pm_score': 3, 'selected': False, 'text': '<p>For .NET application I choose "<a href="http://msdn.microsoft.com/en-us/magazine/cc163329.aspx" rel="nofollow noreferrer">.NET Parallel Extensions (PLINQ)</a>" it\'s extremely easy to use and allows me to parallelize existing code in minutes.</p>\n\n<ol>\n<li>It\'s simple to learn</li>\n<li>Used to perform complex operations over large arrays of objects, so I can\'t comment on other applications</li>\n<li>Supports tasks and piplines</li>\n<li>Should be supported for a next couple of years, but who knows for sure?</li>\n<li>CTP version has some performance issues, but already looks very promising.</li>\n</ol>\n\n<p>Mono <a href="http://tirania.org/blog/archive/2008/Jul-26-1.html" rel="nofollow noreferrer">will likely get</a> support for PLINQ, so it could be a cross-platform solution as well.</p>\n'}, {'answer_id': 80034, 'author': 'fulmicoton', 'author_id': 446497, 'author_profile': 'https://Stackoverflow.com/users/446497', 'pm_score': 1, 'selected': False, 'text': '<p>Qt concurrent offers an implementation of MapReduce for multicore which is really easy to use. It is multiOS.</p>\n'}, {'answer_id': 80037, 'author': 'zurk', 'author_id': 14907, 'author_profile': 'https://Stackoverflow.com/users/14907', 'pm_score': 0, 'selected': False, 'text': "<p>I'd use Java - its portable so future processors wont be a problem. I'd also code my application with layers seperating interface / logic and data (more like  3 tier web app) with standard mutex routines as a library (less debugging of parallel code). Remember that web servers scale to many processors really well and are the least painful path to multicore. Either that or look at the old Connection Machine model with a virtual processor tied to data.  </p>\n"}, {'answer_id': 80039, 'author': 'Daniel Papasian', 'author_id': 7548, 'author_profile': 'https://Stackoverflow.com/users/7548', 'pm_score': 4, 'selected': False, 'text': "<p>The mapreduce/hadoop paradigm is useful, and relevant.  Especially for people who are used to languages like perl, the idea of mapping over an array and doing some action on each element should come pretty fluidly and naturally, and mapreduce/hadoop just takes it to the next stage and says that there's no reason that each element of the array need be processed on the same machine.  </p>\n\n<p>In a sense it's more battle tested, because Google is using mapreduce and plenty of people have been using hadoop, and has shown that it works well for scaling applications across multiple machines over the network.  And if you can scale over multiple machines across the network, you can scale over multiple cores in a single machine.</p>\n"}, {'answer_id': 80044, 'author': 'Allen', 'author_id': 6043, 'author_profile': 'https://Stackoverflow.com/users/6043', 'pm_score': 2, 'selected': False, 'text': '<p>I\'m betting on communicating event loops with promises, as realized in systems like <a href="http://twistedmatrix.com" rel="nofollow noreferrer">Twisted</a>, <a href="http://erights.org" rel="nofollow noreferrer">E</a>, <a href="http://prog.vub.ac.be/amop/start" rel="nofollow noreferrer">AmbientTalk</a>, and others. They retain the ability to write code with the same execution-model assumptions as non-concurrent/parallel applications, but scaling to distributed and parallel systems. (That\'s why I\'m working on <a href="http://launchpad.net/ecru" rel="nofollow noreferrer">Ecru</a>.)</p>\n'}, {'answer_id': 80064, 'author': 'Kevin Little', 'author_id': 14028, 'author_profile': 'https://Stackoverflow.com/users/14028', 'pm_score': 2, 'selected': False, 'text': '<p>Check out <a href="http://www.erlang.org/" rel="nofollow noreferrer">Erlang</a>. Google for it, and watch the various presentations and videos. Many of the programmers and architects I respect are quite taken with its scalability. We\'re using it where I work pretty heavily...</p>\n'}, {'answer_id': 80079, 'author': 'Thomas', 'author_id': 14637, 'author_profile': 'https://Stackoverflow.com/users/14637', 'pm_score': 3, 'selected': False, 'text': '<p>For heavy computations and the like, purely functional languages like <a href="http://www.haskell.org/" rel="noreferrer">Haskell</a> are easily <a href="http://www.macs.hw.ac.uk/~dsg/gph/" rel="noreferrer">parallelizable</a> without any effort on the part of the programmer. Apart from learning Haskell, that is.</p>\n\n<p>However, I do not think that this is the way of the (near) future, simply because too many programmers are too used to the imperative programming paradigm.</p>\n'}, {'answer_id': 80150, 'author': 'Daniel', 'author_id': 13615, 'author_profile': 'https://Stackoverflow.com/users/13615', 'pm_score': 1, 'selected': False, 'text': '<p>If your problem domain permits try to think about a share nothing model. The less you share between processes and threads the less you have to design complex concurrency models.</p>\n'}, {'answer_id': 80176, 'author': 'Jörg W Mittag', 'author_id': 2988, 'author_profile': 'https://Stackoverflow.com/users/2988', 'pm_score': 4, 'selected': False, 'text': '<p>Two solutions I really like are <a href="http://Moscova.Inria.Fr/join/" rel="noreferrer">join calculus</a> (<a href="http://JoCaml.Inria.Fr/" rel="noreferrer">JoCaml</a>, <a href="http://Research.Microsoft.Com/~nick/polyphony/" rel="noreferrer">Polyphonic C#</a>, <a href="http://Research.Microsoft.Com/COmega/" rel="noreferrer">Cω</a>) and the <a href="http://Wikipedia.Org/wiki/Actor_model" rel="noreferrer">actor model</a> (<a href="http://Erlang.Org/" rel="noreferrer">Erlang</a>, <a href="http://Scala-Lang.Org/" rel="noreferrer">Scala</a>, <a href="http://ERights.Org/" rel="noreferrer">E</a>, <a href="http://IoLanguage.Com/" rel="noreferrer">Io</a>).</p>\n\n<p>I\'m not particularly impressed with <a href="http://Wikipedia.Org/wiki/Software_transactional_memory" rel="noreferrer">Software Transactional Memory</a>. It just feels like it\'s only there to allow threads to cling on to life a little while longer, even though they should have died decades ago. However, it does have three major advantages:</p>\n\n<ol>\n<li>People understand transactions in databases</li>\n<li>There is already talk of transactional RAM hardware</li>\n<li>As much as we all wish them gone, threads are probably going to be the dominant concurrency model for the next couple of decades, sad as that may be. STM could significantly reduce the pain.</li>\n</ol>\n'}, {'answer_id': 84271, 'author': 'emk', 'author_id': 12089, 'author_profile': 'https://Stackoverflow.com/users/12089', 'pm_score': 5, 'selected': False, 'text': '<p>Multi-core programming may actually require more than one paradigm. Some current contenders are:</p>\n\n<ol>\n<li><a href="http://labs.google.com/papers/mapreduce.html" rel="noreferrer">MapReduce</a>. This works well where a problem can be easily decomposed into parallel chunks.</li>\n<li><a href="http://www.haskell.org/haskellwiki/GHC/Data_Parallel_Haskell" rel="noreferrer">Nested Data Parallelism</a>. This is similar to MapReduce, but actually supports recursive decomposition of a problem, even when the recursive chunks are of irregular size. Look for NDP to be a big win in purely functional languages running on massively parallel but limited hardware (like GPUs).</li>\n<li><a href="http://en.wikipedia.org/wiki/Software_transactional_memory" rel="noreferrer">Software Transactional Memory</a>. If you need traditional threads, STM makes them bearable. You pay a 50% performance hit in critical sections, but you can scale complex locking schemes to 100s of processors without pain. This will not, however, work for distributed systems.</li>\n<li><a href="http://www.erlang.org/" rel="noreferrer">Parallel object threads with messaging</a>. This really clever model is used by Erlang. Each "object" becomes a lightweight thread, and objects communicate by asynchronous messages and pattern matching. It\'s basically true parallel OO. This has succeeded nicely in several real-world applications, and it works great for unreliable distributed systems.</li>\n</ol>\n\n<p>Some of these paradigms give you maximum performance, but only work if the problem decomposes cleanly. Others sacrifice some performance, but allow a wider variety of algorithms. I suspect that some combination of the above will ultimately become a standard toolkit.</p>\n'}, {'answer_id': 84547, 'author': 'smo', 'author_id': 16080, 'author_profile': 'https://Stackoverflow.com/users/16080', 'pm_score': 2, 'selected': False, 'text': '<p>As mentioned, purely functional languages are inherently parallelizable. However, imperative languages are much more intuitive for many people, and we are deeply entrenched in imperative legacy code. The fundamental issue is that pure functional languages express side-effects explicitly, while side effects are expressed implicitly in imperative languages by the order of statements.</p>\n\n<p>I believe that techniques to declaratively express side effects (e.g., in an object oriented framework) will allow compilers to decompose imperative statements into their functional relationships. This should then allow the code to be automatically parallelized in much the same way pure functional code would be.</p>\n\n<p>Of course, just as today it is still desirable to write certain performance-critical code in assembly language, it will still be necessary to write performance-critical explicitly parallel code tomorrow. However, techniques such as I outlined should help automatically take advantage of manycore architectures with minimal effort expended by the developer.</p>\n'}, {'answer_id': 89155, 'author': 'Sam Hasler', 'author_id': 2541, 'author_profile': 'https://Stackoverflow.com/users/2541', 'pm_score': 3, 'selected': False, 'text': '<p><a href="http://www.kamaelia.org/Home" rel="nofollow noreferrer">kamaelia</a> is a <strong>python framework</strong> for building applications with lots of communicating processes.</p>\n<blockquote>\n<h1><img src="https://i.stack.imgur.com/I1E9Z.png" width="100" height="93"> Kamaelia - Concurrency made useful, fun</h1>\n<p>In Kamaelia you build systems from <strong>simple components that talk to each other</strong>. This speeds development, massively aids maintenance and also means you <strong>build naturally concurrent software</strong>. It\'s intended to be accessible by <strong>any</strong> developer, including novices. It also makes it fun :)</p>\n<p>What sort of systems? Network servers, clients, desktop applications, pygame based games, transcode systems and pipelines, digital TV systems, spam eradicators, teaching tools, and a fair amount more :)</p>\n</blockquote>\n<p>See also the Question <a href="https://stackoverflow.com/questions/121674/multi-core-and-concurrency-languages-libraries-and-development-techniques">Multi-Core and Concurrency - Languages, Libraries and Development Techniques</a></p>\n'}, {'answer_id': 229336, 'author': 'Sam Hasler', 'author_id': 2541, 'author_profile': 'https://Stackoverflow.com/users/2541', 'pm_score': 1, 'selected': False, 'text': '<p>See also the question <a href="https://stackoverflow.com/questions/121674/multi-core-and-concurrency-languages-libraries-and-development-techniques">Multi-Core and Concurrency - Languages, Libraries and Development Techniques</a></p>\n'}, {'answer_id': 229360, 'author': 'AlePani', 'author_id': 24276, 'author_profile': 'https://Stackoverflow.com/users/24276', 'pm_score': 0, 'selected': False, 'text': '<p>Erlang is the more "mature solution" and is portable and open source. I fiddled around with Polyphonic C# , I don\'t know how it would be to program everyday in it.</p>\n\n<p>There are libraries and extensions for almost every language/OS under the sun, google transactional memory . It\'s an interesting approach from MS.</p>\n'}, {'answer_id': 658601, 'author': 'Jed', 'author_id': 33208, 'author_profile': 'https://Stackoverflow.com/users/33208', 'pm_score': 2, 'selected': False, 'text': "<p>I'm surprised nobody has suggested MPI (Message Passing Interface).  While designed for distributed memory, MPI programs with essential and frequent global coupling (solving linear and nonlinear equations with billions of unknowns) have been shown to scale to 200k cores.</p>\n"}, {'answer_id': 940345, 'author': 'Paul Morrison', 'author_id': 408718, 'author_profile': 'https://Stackoverflow.com/users/408718', 'pm_score': 2, 'selected': False, 'text': '<p>This question seems to keep appearing with different wording - maybe there are different constituencies within StackOverflow.  <a href="http://www.jpaulmorrison.com/fbp/" rel="nofollow noreferrer"> Flow-Based Programming </a> (FBP) is a concept/methodology that has been around for over 30 years, and is being used to handle most of the batch processing at a major Canadian bank. It has thread-based implementations in Java and C#, although earlier implementations were fiber-based (C++, and mainframe Assembler - the one used at the bank).  Most approaches to the problem of taking advantage of multicore involve trying to take a conventional single-threaded program and figure out which parts can run in parallel.  FBP takes a different approach: the application is designed from the start in terms of multiple "black-box" components running asynchronously (think of a manufacturing assembly line).  Since the interface between components is data streams, FBP is essentially language-independent, and therefore supports mixed-language applications, and domain-specific languages.  For the same reason, side-effects are minimized.  It could also be described as a "share nothing" model, and a MOM (message-oriented middleware).  MapReduce seems to be a special case of FBP. FBP differs from Erlang mostly in that Erlang operates in terms of many short-lived threads, so green threads are more appropriate there, whereas FBP uses fewer (typically a few 10s to a few hundred) longer-lived threads. For a <em>piece</em> of a batch network that has been in daily use for over 30 years, see <a href="http://www.jpaulmorrison.com/fbp/image010.jpg" rel="nofollow noreferrer"> part of batch network</a>.  For a high-level design of an interactive app, see <a href="http://www.jpaulmorrison.com/cgi-bin/wiki.pl?BrokerageApplication" rel="nofollow noreferrer"> Brokerage app high-level design</a>. FBP has been found to result in much more maintainable applications, and improved elapsed times - even on single core machines!</p>\n'}, {'answer_id': 940631, 'author': 'dbr', 'author_id': 745, 'author_profile': 'https://Stackoverflow.com/users/745', 'pm_score': 1, 'selected': False, 'text': "<p>A job-queue with multiple workers system (not sure of the correct terminology - message queue?)</p>\n\n<p>Why?</p>\n\n<p>Mainly, because it's an absurdly simple concept. You have a list of stuff that needs processing, then a bunch of processes that get jobs and process them.</p>\n\n<p>Also, unlike the reasons, say, Haskell or Erlang are so concurrent/parallelisable(?), it's entirely language-agnostic - you can trivially implement such a system in C, or Python, or any other language (even using shell scripting), whereas I doubt bash will get software transactional memory or join-calculus anytime soon.</p>\n"}, {'answer_id': 1344944, 'author': 'Ira Baxter', 'author_id': 120163, 'author_profile': 'https://Stackoverflow.com/users/120163', 'pm_score': 1, 'selected': False, 'text': '<p>We\'ve been using <a href="http://www.semanticdesigns.com/Products/PARLANSE" rel="nofollow noreferrer">PARLANSE</a>, a parallel programming langauge with explicit partial-order specification of concurrency for the last decade, to implement a scalable program analysis and transformation system (<a href="http://www.semanticdesigns.com/Products/DMS/DMSToolkit.html" rel="nofollow noreferrer">DMS Software Reengineering Toolkit</a>) that mostly does symbolic rather than numeric computation.   PARLANSE is a compiled, C-like language with traditional scalar data types character, integer, float, dynamic data types string and array, compound data types structure and union, and lexically-scoped functions.   While most of the language is vanilla (arithmetic expressions over operands, if-then-else statements, do loops, function calls), the parallelism is not.  Parallelism  is expressed by defining a "precedes" relation over blocks of code (e.g, a before b, a before c, d before c)\nwritten as</p>\n\n<pre><code>(|;  a  (... a\'s computation)\n     (&lt;&lt; a) b ( ... b\'s computation ... )\n     (&lt;&lt; a) c ( ....c\'s computation ...)\n     (&gt;&gt; c) d ( ... d\'s computation...)\n)|;\n</code></pre>\n\n<p>where the &lt;&lt; and >> operators refer to "order in time".  The PARLANSE compiler can see these parallel computations and preallocate all the structures necessary for computation\ngrains a,b,c,d, and generate custom code to start/stop each one, thus minimizing the overhead to start and stop these parallel grains.</p>\n\n<p>See this link for <a href="https://stackoverflow.com/questions/1862599/multithreading-puzzles/1866161#1866161"> <em>parallel</em> iterative deepening search for optimals solutions to the 15-puzzle</a>, which is the 4x4 big-brother of the 8-puzzle.   It only uses <em>potential</em> parallel as a parallelism construct  <strong>(|| a b c d )</strong> which says there are no partial order constraints on the computations <em>a</em> <em>b</em> <em>c</em> <em>d</em>, but it also uses speculation and asynchronously aborts tasks that won\'t find solutions.  Its a lot of ideas in a pretty small bit of code.</p>\n\n<p>PARLANSE runs on multicore PCs.  A big PARLANSE program (we\'ve built many with 1 million+ lines or more) will have thousands of these partial orders, some of which call functions that contain others.\nSo far we\'ve had good results with up to 8 CPUs, and modest payoff with up to 16, and we\'re still tuning the system.  (We think a real problem with larger numbers of cores on current PCs is memory bandwidth: 16 cores pounding a memory subsystem creates a huge bandwidth demand).</p>\n\n<p>Most other languages don\'t expose the parallelism so it is hard to find, and the runtime systems pay a high price for scheduling computation grains by using general-purpose scheduling primitives.  We think that\'s a recipe for disaster or at least poor performance because of Amhdahl\'s law: if the number of machine instructions to schedule a grain is large compared to the work, you can\'t be efficient.  OTOH, if you insist on computation grains with many machine instructions to keep the scheduling costs relatively low, you can\'t find computation grains that are independent and so you don\'t have any useful parallelism to schedule.   So the key idea behind PARLANSE is to minimize the cost of scheduling grains, so that grains can be small, so that there can be many of them found in real code.   The insight into this tradeoff came from the abject failure of the pure dataflow paradigm, that did everything in parallel with tiny parallel chunks (e.g., the add operator).</p>\n\n<p>We\'ve been working on this on and off for a decade.  Its hard to get this right.  I don\'t see how folks that haven\'t been building parallel langauges and using/tuning them for this time frame have any serious chance of building effective parallel systems. </p>\n'}, {'answer_id': 1344990, 'author': 'Steve Rowe', 'author_id': 40744, 'author_profile': 'https://Stackoverflow.com/users/40744', 'pm_score': 1, 'selected': False, 'text': '<p>I really like the model <a href="http://www.clojure.org" rel="nofollow noreferrer">Clojure</a> has chosen.  Clojure uses a combination of immutable data structures and software transactional memory.</p>\n\n<p>Immutable data structures are ones that never change.  New versions of the structures can be created with modified data, but if you have a "pointer" to a data structure, it will never change out from under you.  This is good because you can access that data without worrying about concurrency problems.</p>\n\n<p>Software transactional memory is discussed elsewhere in these answers but suffice it to say that it is a mechanism whereby multiple threads can all act upon some data and if they collide, one of the threads is rolled back to try again.  This allows for much faster speed when the risk of collision is present but unlikely.</p>\n\n<p>There is a <a href="http://clojure.blip.tv/file/812787" rel="nofollow noreferrer">video</a> from author Rich Hickey that goes into a lot more detail.</p>\n'}, {'answer_id': 1345078, 'author': 'SingleNegationElimination', 'author_id': 65696, 'author_profile': 'https://Stackoverflow.com/users/65696', 'pm_score': 1, 'selected': False, 'text': '<p>A worthwhile path might be <a href="http://en.wikipedia.org/wiki/OpenCL" rel="nofollow noreferrer">OpenCL</a>, which provides a means of distributing certain kinds of compute loads across heterogeneous compute resources, IE the same code will run on a multicore CPU and also commodity GPU\'s.  ATI has recently released exactly such a <a href="http://ati.amd.com/technology/streamcomputing/opencl.html" rel="nofollow noreferrer">toolchain</a>.  NVidia\'s <a href="http://www.nvidia.com/object/cuda_home.html" rel="nofollow noreferrer">CUDA</a> toolchain is similar, although somewhat more restricted.  It also appears that Nvidia has an OpenCL sdk <a href="http://www.nvidia.com/object/cuda_opencl.html" rel="nofollow noreferrer">in the works</a></p>\n\n<p>It should be noted that this probably won\'t help much where the workloads are not of a data-parallel nature, for instance it won\'t help much with typical transaction processing.  OpenCL is mainly oriented toward the kinds of computing that are math intensive, such as scientific/engineering simulation or financial modeling.</p>\n'}, {'answer_id': 2893087, 'author': 'J D', 'author_id': 13924, 'author_profile': 'https://Stackoverflow.com/users/13924', 'pm_score': 1, 'selected': False, 'text': "<blockquote>\n  <p>If you were writing a new application from scratch today, and wanted it to scale to all the cores you could throw at it tomorrow, what parallel programming model/system/language/library would you choose?</p>\n</blockquote>\n\n<p>Perhaps the most widely applicable today is Cilk-style task queues (now available in .NET 4). They are great for problems that can be solved using divide and conquer with predictable complexity for subtasks (such as parallel <code>map</code> and <code>reduce</code> over arrays where the complexity of the function arguments is known as well as algorithms like quicksort) and that covers many real problems.</p>\n\n<p>Moreover, this only applies to shared-memory architectures like today's multicores. While I do not believe this basic architecture will disappear anytime soon I do believe it must be combined with distributed parallelism at some point. This will either be in the form of a cluster of multicores on a manycore CPU with message passing between multicores, or in the form of a hierarchy of cores with predictable communication times between them. These will require substantially different programming models to obtain maximum efficiency and I do not believe much is known about them.</p>\n"}, {'answer_id': 5943908, 'author': 'Aater Suleman', 'author_id': 745944, 'author_profile': 'https://Stackoverflow.com/users/745944', 'pm_score': 1, 'selected': False, 'text': '<p>There are three parts to parallel programming IMO: identify the parallelism and specify the parallelism. Identify=Breaking down the algorithm into concurrent chunks of work, specify=doing the actual coding/debugging. Identify is independent of which framework you will use to specify the parallelism and I don\'t think a framework can help there much. It comes with good understanding of your app, target platform, common parallel programming trade-offs (hardware latencies etc), and MOST importantly experience. Specify however can be discussed and this is what I try to answer below: </p>\n\n<p>I have tried many frameworks (at school and work). Since you asked about multicores, which are all shared memory, I will stick with three shared memory frameworks I have used. </p>\n\n<p>Pthreads (Its no really a framework, but definitely applicable):</p>\n\n<p>Pro: \n-Pthreads is extremely general. To me, pthreads are like the assembly of parallel programming. You can code any paradigm in pthreads. \n-Its flexible so you can make it as high performance as you want. There are no inherent limitations to slow you down. You can write your own constructs and primitives and get as much speed as possible.</p>\n\n<p>Con: \n-Requires you to do all the plumbing like managing work-queues, task distribution, yourself. \n-The actual syntax is ugly and your app often has a lot of extra code which makes code hard to write and then hard to read. </p>\n\n<p>OpenMP:</p>\n\n<p>Pros:\n-The code looks clean, plumbing and task-splitting is mostly down under the hood\n-Semi-flexible. It gives you several interesting options for work-scheduling </p>\n\n<p>Cons:\n-Meant for simple for-loop like parallelism. (The newer Intel verion does support tasks too but the tasks are the same as Cilk). \n-Underlying structures may or may not be well-written for performance. GNUs implementation is just ok. Intel\'s ICC worked better for me but I would rather write some stuff myself for higher performance. </p>\n\n<p>Cilk, Intel TBB, Apple GCD:</p>\n\n<p>Pros: \n-Provably optimal underlying algorithms for task-level parallelism \n-Decent control of serial/parallel tasks\n-TBB also has a pipeline parallelism framework which I used (it isn\'t the best to be frank)\n-Eliminates the task of writing a lot of code for task-based systems which can be a big plus if you are short-handed </p>\n\n<p>Cons: \n-Less control of underlying structures\' performance. I know the Intel TBB has very poorly-performing underlying data structures, e.g., the work queue was bad (in 2008 when I saw it). \n-The code looks awful sometimes with all the keywords and buzzwords they want you to use\n-Requires reading a lot of references to understand their "flexible" APIs</p>\n'}]
['https://Stackoverflow.com/questions/79999', 'https://Stackoverflow.com', 'https://Stackoverflow.com/users/171236/']