Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Multicore COBOL: three approaches

Multicore COBOL: three approaches

Richard O'Keefe - University of Otago - Nov'12

Presented at Multicore World 2013, February 20, 2013

COBOL is a commercially important historic programming language. This survey of ways to adapt it to a multicore world shows that support for parallelism can be added below a language, in it, or above it, and COBOL is a good example. It turns out that concurrent COBOL has been a commercially successful reality for 43 years.

Multicore World 2013

February 15, 2013
Tweet

More Decks by Multicore World 2013

Other Decks in Programming

Transcript

  1. Multicore COBOL: three approaches Richard A. O’Keefe November 2012 Abstract

    COBOL is a commercially important historic programming language. This survey of ways to adapt it to a multicore world shows that support for parallelism can be added below a language, in it, or above it, and COBOL is a good example. It turns out that concurrent COBOL has been a commercially successful reality for 43 years. 1 Introduction COBOL[1] is a programming language for business applications, originally de- vised in late 1959, and revised several times since. C. A. R. Hoare wrote[9] that COBOL 68 was “a language suitable for sim- ple applications in business data processing. It contains good data structuring capability, but poor facilities for abstraction. It aimed at readability, but unfor- tunately achieved only prolixity; it aimed to provide a complete programming tool, in a way few languages have since.” Since then, revisions of COBOL have added support for structured pro- gramming (1985) and even object-oriented programming (2002), so the charge of “poor facilities for abstraction” has long ceased to be fair. The prolixity of COBOL has not, however, decreased. There is a great deal of COBOL in the world, representing a huge investment of time and money to produce it, which is still of enough value in what it does that people want to continue using it. According to [10] (which should be read for the sources to these claims): According to a 2009 survey commissioned by Micro Focus and con- ducted by Harris Interactive: • 70–75% of the business and transaction systems around the world run on COBOL. This includes credit card systems, ATMs, ticket purchasing, retail/POS systems, banking, payroll sys- tems, telephone/cell calls, grocery stores, hospital systems, gov- ernment systems, airline systems, insurance systems, automo- tive systems, traffic signal systems. • 90% of global financial transactions are processed in COBOL. 1
  2. • The language supports over 3 × 1010 transactions per

    day. • The average American still interacts with a COBOL program 13 times a day. • There are 1.5-2 million developers, globally, working with COBOL code. • There are around 2 × 1011 lines of COBOL code in use. • Around 5 × 109 lines of new COBOL code are added to live systems every year. • The investment made into COBOL systems over the past 50 years is said to be worth about USD 2 × 1012 . Much of this software was written for old single-processor machines and ran under old operating systems that we used to think of as huge but now see as tiny, machines and systems that differed greatly from one another. Nothing could “stop the march of the killer micros”, and now we have many manufacturers mostly making one of two machines: an ARM device with something vaguely UNIXish or Windowsish or an x86 or x86-64 device ditto. Workloads are often higher too. Clusters and RAID let commodity micropro- cessors approach the I/O capacity of mainframes, but this requires distributed, or at least concurrent processing. Stock microprocessors these days are gener- ally multicore. But none of the revisions to the COBOL standard has added concurrency support. Software gets to be legacy software by remaining useful. How can the useful- ness of this COBOL software be prolonged into an era of multicore commodity machines following multivendor standards like POSIX[2]? We can do this below the language (helping existing code), above the lan- guage (using existing code), or inside the language (requiring new or revised code). This applies to any programming language, so this paper should be of use even to people who are not interested in COBOL as such. Changes inside the language are of interest for new or actively maintained code. Changes above and below the language are of more interest to people who want to improve the performance of existing code while developing a replacement. 2 Instead of the Language Organisations are often advised to convert their COBOL code to a “modern” language like Java or C#. There are businesses providing such a service, and even free tools. It’s actually not unfair to compare COBOL to AWK. “From an arithmetic point of view, one seldom encounters a multiplication or division in business programming, let alone anything as arcane as a square root. The vast majority of business applications do such things as transforming data from one format 2
  3. to another, accumulating totals or looking up information in one

    file and incor- porating it into another file or a report.” [8, chapter 1]. AWK is a UNIX scripting language that hasn’t been modernised much. It’s nowhere near as expressive as Perl, for example. The a2p program will convert AWK to Perl. But it seldom makes sense to do so, because Perl is noticeably slower than AWK at the things AWK can do and rather less maintainable. While COBOL is bad (to very bad) at the things that Java and C# are good at, the opposite is also true. In particular, COBOL offers tight control over high precision decimal arithmetic, and mainframe compilers are very good at generating code for such arithmetic. For example, a simple compute Total = Total + Quantity * Unit-Price. in COBOL, compiling to a few in-line instructions, becomes total = total.add(unit_price.multiply(quantity)); in Java, implying at least two object allocations. Part of the problem is that BigDecimal discovers at run time what a COBOL compiler knows at compile time, but cannot tell the Java compiler. It is certainly possible to generate less inefficient Java code for arithmetic (but lacking access to even hardware facilities like the overflow flag, truly efficient code is probably beyond expression in Java), but in general code that faithfully preserves the semantics of a COBOL source will be COBOL code, just written in Java syntax, combining the maintainability problems of both languages. There are two tolerably well-known languages offering concurrency and good decimal arithmetic: PL/I and Ada. The availability of GNAT1 makes Ada an attractive conversion target. Not, however, a popular one. Switching languages is never an easy or a cheap task. 3 Below the Language Changes below the language are changes to the way the compiler generates code or to the way associated libraries are built which require no changes to the source code and except for abstraction-breaking tools like debuggers and profilers make no change to the semantics of the program. The old dream of automatically parallelising “dusty deck” code without changing a single line is still with us. Intel’s C compiler and gcc now routinely vectorise simple code using the short-vector graphics-oriented instructions on modern machines (NEON, SSE). In practice, this is often disappointing: I was excited when icc told me about all the loops it was vectorising until I realised that they were initialisation loops that already took too small a proportion of my programs’ time to make any difference. Expecting a compiler to get all the parallelism possible for a task without any help from the programmer is unrealistic. 1The free Ada compiler from https://www.adacore.com/ 3
  4. However, getting a compiler to do some things better without

    a huge compiler development effort is practical. Loops are often handled by looking for instances of well bounded easily recognised “chunks” that have the potential to take a lot of time. It is possible to exploit multimedia instructions even in basic blocks: see for example “Autovectorization in LLVM”[11]. 3.1 Vectorising COBOL has a selection of “intrinsic functions” The intrinsic functions MAX, MIN, MEAN, ORD-MAX, ORD-MIN, RANGE, STANDARD-DEVIATION, SUM, VARIANCE already exist. Intrinsic functions that allow a variable num- ber of arguments already allow the use of arrays with one or more ALL sub- scripts; this has been so since COBOL-85. These are reduction operations which can clearly be evaluated in parallel. 3.2 Special Statements COBOL has special statements for • SORTing internal tables, external files, and data streams generated or consumed by user-defined code. The implementation of this should be able to exploit large memories and multicore. • MERGEing external files. The output may be consumed by user-defined code, but the input must be sorted files. There are parallel merge proce- dures that could be used. • string processing. There are some cases of the INSPECT and SEARCH statements that could be parallelised. Probably not worth it. • data VALIDATion — fields of a complex record structure can be indepen- dently checked and implicitly moved to other variables. A compiler would probably convert this to ordinary loops which could be parallelised like other simple loops. • database access. COBOL has had an SQL interface for nearly as long as SQL has existed. It is part of the SQL standard. If a database implemen- tation makes good use of multicore, many COBOL programs will benefit indirectly. 3.3 Input-Output COBOL programs often spend a lot of their time reading and writing exter- nal files. A file may be declared to be sequential, relative (records have an integer relative record number and may be sequentially or randomly accessed), or indexed (having a primary key which excludes duplicates and possibly al- ternate keys which may allow duplicates, records being accessed in key order 4
  5. or randomly). A sequential file may be generated using the

    “Report Writer” feature. Reading or writing a file requires things like system calls, blocking/unblocking records, padding/unpadding, character set conversion, and conversion of data from external formats to internal formats. COBOL was designed to work with external formats directly, so you can do arithmetic on character-per-digit or nybble-per-digit data, but on an x86(-64), only adding, subtracting, and com- paring can really be done that way; anything else requires conversion. If you think of a sequential file as a coroutine, it is clear that all of this work can be done for a file by a separate thread. This would be particularly appropriate for reports. The opportunity for parallelism here is real but limited. Consider a loop SET TIME-TO-STOP TO FALSE. PERFORM UNTIL TIME-TO-STOP READ INPUT AT END SET TIME-TO-STOP TO TRUE NOT AT END PERFORM SOME-COMPUTATION WRITE OUTPUT END-READ END-PERFORM. With variable-length records, it would be very difficult to parallelise this. Fortunately, COBOL was designed in the era of unit-record equipment, so its logical records are usually fixed in size. This means that the loop could be divided into p parts, each knowing exactly where to start in the input and the output. Provided SOME-COMPUTATION does not depend on the initial values of variables it uses other than those in INPUT and nothing depends on the final values of those variables, each part can be given its own copy of that workspace. The I/O bandwidth to a single mass storage device may well be a limiting factor. It can only be dealt with by having more than one such device.2 Striping the records of a file across multiple devices would be one way to exploit that. 3.4 Libraries COBOL programs can call components written in other languages. If those components exploit multicore, the whole program benefits. 3.5 A problem with CALL A COBOL “run unit” is a bundle of “program”s. Some of them are separately compiled. Since COBOL 1985, some of them are nested inside other programs. 2Yes, I am talking about RAID. 5
  6. A program has some files, some data, and some code.

    The PERFORM state- ment has two uses, one as a loop, and the other as a sort of procedure call within a program. However, PERFORM is closer to BASIC’s GOSUB than to a C function or Java method call; the caller and callee have all of their variables in common. There is also a way for one “program” to call another. These calls may pass parameters by value or reference, and the caller and callee have their own variables. However, to put it in C terms, all the variables of a program are static. When the called program returns to its caller, all its variables continue to exist, and the next time it is called those variables are expected to retain their values from the last call. This means that you cannot take a sequence of calls — even ones with disjoint parameters — and execute them in parallel. Of course a compiler could determine by data-flow analysis that initial values are not used. There is an exception. If you declare PROGRAM-ID. name IS INITIAL PROGRAM. then whenever that program is called, its variables (and the variables of any further programs it contains) should be reset to their initial values. Calls to such program can be executed in parallel, under the same conditions as C functions. The snag is that a COBOL program can CALL another program that will be in the same run unit at run time without any information about that program being in scope, so other things being equal, you cannot tell from the outside whether a program is an INITIAL program (or has been determined by the compiler to be like one) or not. As older C programmers know very well, a procedure calling mechanism where there is no compile-time check that the types and transfer modes of the actual parameters match the types and transfer modes of the formal parameters can be a very dangerous thing. So a tool that maintains a repository of interface information about programs in a suite, which a compiler or source to source transformer can read, would be a good thing to have anyway. COBOL 2002 includes prototypes. 4 In the language 4.1 Vectorising We could add three more intrinsic functions, all taking conditions as arguments: FUNCTION ANY ( conditional-expression ) FUNCTION ALL ( conditional-expression ) FUNCTION COUNT ( conditional-expression ) We could extend the arithmetic statements to operate on arrays. Existing syntax: ADD-statement = ADD source+ TO destination+ 6
  7. [exception-handling] [END-ADD] | ADD source+ TO source GIVING destination+ [exception-handling]

    [END-ADD] | ADD-CORRESPONDING-statement SUBTRACT-statement = SUBTRACT source+ FROM destination+ [exception-handling] [END-SUBTRACT] | SUBTRACT source+ FROM source GIVING destination+ [exception-handling] [END-SUBTRACT] | SUBTRACT-CORRESPONDING-statement; MULTIPLY-statement = MULTIPLY source BY destination+ [exception-handling] [END-MULTIPLY] | MULTIPLY source BY source GIVING destination+ [exception-handling] [END-MULTIPLY]; DIVIDE-statement = DIVIDE source INTO destination+ [exception-handling] [END-DIVIDE] | DIVIDE source INTO source GIVING destination+ [exception-handling] [END-DIVIDE] | DIVIDE source BY source GIVING destination+ [exception-handling] [END-DIVIDE] | DIVIDE source INTO source GIVING destination REMAINDER variable [exception-handling] [END-DIVIDE] | DIVIDE source BY source GIVING destination REMAINDER variable [exception-handling] [END-DIVIDE]; MOVE-statement = MOVE source TO variable+ | MOVE-CORRESPONDING-statement; COMPUTE-statement = COMPUTE destination+ = arithmetic-expression [exception-handling] [END-COMPUTE] | COMPUTE variable+ = boolean-expression [END-COMPUTE]; source = variable | constant; destination = variable [rounding]; exception-handling 7
  8. = [[ON] SIZE ERROR imperative-statement] [NOT [ON] SIZE ERROR imperative-statement];

    In standard COBOL, each of the variables mentioned in these statements must refer to a scalar; arrays must be fully subscripted. Supporting vectorised arithmetic requires just two changes • allow some of the variables in these statements to have ALL subscripts, provided that all of the variables that have any ALL subscripts have the same number, and • require that matching ALL subscripts have the same range, adding a new kind of exception to catch mismatch: exception-handling = [[ON] DIMENSION ERROR imperative-statement] [[ON] SIZE ERROR imperative-statement] [NOT [ON] SIZE ERROR imperative-statement]; This permits both the use of SIMD instructions and the use of OpenMP[3,4,5]- style concurrent loops, just like array-at-a-time operations in Fortran 90 and later. 4.2 POSIX Threads It is obviously possible to create a binding to POSIX Threads. There was a time when COBOL had an advantage over C for such a binding: there were no recursive procedures, no dynamic allocation, and no dynamically sized arrays, so it was straightforward for a COBOL compiler to determine exactly how much storage a PROGRAM needed. Those features all now exist. Modern COBOL is even object-oriented3. There are two views of what a binding should do. One view is that it should expose every detail of the underlying facility to the host language (“thin binding”). The other is that it should provide an analogue of the facility that feels natural in the host language and uses other concepts and facilities of the host language where possible (“thick binding”). The special advantage of a thick binding to threads is that the compiler can know what is happening and can check more things. 4.2.1 Creating threads COBOL has two kinds of procedures. Those present from the early days do not have parameters or local variables. Those added in 1985 have both of those things, so we would extend the PROGRAM construct with PROGRAM-ID. program-name-1 [AS literal-1] IS CONCURRENT PROGRAM. 3Now with twice the verbosity and half the clarity! 8
  9. and the CALL statement with CALL (identifier-1|literal-1) [USING arguments] CONCURRENT

    [options] [RETURNING identifier-3] [END-CALL] 4.2.2 Atomic updates Operating systems have provided C with library support for atomic operations on hardware numbers for quite some time, and the C11 standard now includes such features in stdatomic.h . The problem is that while COBOL numbers may correspond directly to hardware numbers, they very often do not. In particular, in modern COBOL a compiler must support numbers with up to 31 decimal digits. Such numbers do not correspond to anything a Pentium can do with the LOCK prefix. The simplest approach is to add an optional [IS] ATOMIC clause to the description of elementary data items. The effect is that any fetch of the variable should be atomic, any store of the variable should be atomic, and any ADD or SUBTRACT statement where the variable is both fetched and stored should be atomic. An atomic ADD or SUBTRACT statement should have a single destination, so that statements like ADD x TO y GIVING x y. would be forbidden to avoid potential deadlocks. Since atomic updates to even 31-digit numbers should be very quick, locking could be done with spin-locks using “test and test-and-set”, which would have only one byte of memory overhead, or “compare-and-swap” with four bytes of overhead. We would expect a compiler to have an option to inform the programmer about which atomic variables could be handled by hardware operations and which needed locks. One important use of atomic variables is for compare-and-swap. A new intrinsic function FUNCTION COMPARE-AND-SWAP(atomic-var, old-value, new-value) seems to be the simplest interface. Once again we have the problem that the hardware will support either Compare-And-Swap (as SPARC and x86 do) or Load Locked/Stored Conditional (as PowerPC and ARM do) for this operation on machine words, but that COBOL variables need not be word sized. Rather than force COBOL programmers to use unnatural data types, the compiler should synthesise COMPARE-AND-SWAP using whatever it needs to, option- ally warning the programmer if this is inefficient. 9
  10. 4.2.3 Mutexes and read-write locks In a thick binding, instead

    of providing direct access to POSIX mutexes and read-write locks, we could build them into the syntax of data structures. Unless a variable is declared GLOBAL, it is private to the program that contains it, so we add to the GLOBAL clause level-number [entry-name-clause] ... [IS] GLOBAL [WITH [READ WRITE] LOCK] ... Files are also private to a program (hence private to a thread) unless declared GLOBAL, so the same lock declaration would apply to them. Add a new form of PERFORM statement: PERFORM WITH [READ | WRITE] LOCK imperative-statements END-PERFORM The compiler could check that the data in that group were not accessed except in the scope of a PERFORM with LOCK, providing a safety guarantee painfully absent from C. POSIX specifies that each FILE object in C has a lock and that each opera- tion on a FILE except getc_unlocked and putc_unlocked acquires and releases that lock. This meant a huge performance hit for old efficient character-I/O- intensive code, and a false sense of security. Anything requiring the use of say a loop to print several items in a line requires the use of flockfile and funlockfile. The approach taken here is that a file does not have a lock unless it is declared to be a global file with a lock, so there is no performance hit for old code. A nested concurrent program may not use a global file unless that file is declared to have a lock. A file with a lock may not be operated on except within a PERFORM WITH LOCK statement. This means that all locking is explicit and all unlocked I/O is safe. Two problems with conditions in POSIX are that they are not intrinsically associated with mutexes and that “on a multi-processor, it may be impossible for an implementation of pthread_cond_signal() to avoid the unblocking of more than one thread” so that “applications are forced to code a predicate- testing-loop around the condition wait”. We can fix both problems in a thick binding by (1) declaring condition variables only inside records that have locks and (2) associating a predicate with a condition in its declaration. E.g., 01 My-Protected-Data IS GLOBAL WITH LOCK. 02 Available-Data-Count PIC 9(2). ... 02 Data-Is-Available CONDITION (Available-Data-Count IS NOT ZERO). One can then simply await a condition and the compiler can generate the loop. 10
  11. 4.2.4 Channels The POSIX thread interface is oriented towards threads

    that communicate through shared memory. There is another approach, exemplified by Occam and Erlang [7] and Go, which is to communicate by sending and receiving mes- sages through some kind of channel. Given how systems of COBOL programs communicate, this is a fairly natural approach for a COBOL programmer. There is no “bounded buffer” facility in the POSIX thread library, but a “thick binding” approach may reasonably provide one. Specify a bounded buffer in the FILE-CONTROL section: SELECT file-name-1 ORGANIZATION IS CONCURRENT [RESERVE integer-1 [AREA|AREAS]]. This indicates that file-name-1 is a bounded buffer. The format of the records is defined later in the program, as always in COBOL. The number of records is given by integer-1. If integer-1 is not specified, this is an Occam-style synchronous channel where communication happens when one thread wants to write and another thread wants to read. If integer-1 is specified, it must be positive, and you get an asynchronous channel with the specified capacity. The only operations on a channel are READ and WRITE, which must not be in PERFORM WITH LOCK. A channel must be declared GLOBAL, otherwise it would be pointless. 4.3 OpenMP OpenMP[3,4,5] is a “consortium of HPC vendors, national labs, supercomputing centers, academic institutions and users” producing specifications for C, C++, and Fortran extensions to support parallel programming. Anyone familiar with COBOL and OpenMP will find combining them an obvious idea. In fact, it is so obvious that it is patented[6]. OpenMP is a good fit to COBOL 85 because the control structures and data structures of COBOL are similar to the control structures and data structures of C 99 and Fortran 95 — OpenMP’s main targets — except that COBOL 85 has more complicated numbers (not a problem for OpenMP) and no pointers (a relief for OpenMP). Translating COBOL with OpenMP annotations to C could be done in a structure-preserving way with the OpenMP annotations going along for the ride. There are two main approaches one might take: OpenMP-in-the-target and OpenMP-in-the-source. The patent appears to envisage either discovering par- allelism in a COBOL program automatically or having it manually annotated in some other fashion, and generating code in some other language using OpenMP in that other language. This is OpenMP-in-the-target. The other approach is to devise an OpenMP binding to use in COBOL source code, which could be translated with C with OpenMP or to something 11
  12. that does not involve OpenMP at all. The main issue

    is that handling I/O is important, and all OpenMP 3.1[4] has to say about I/O is that it is the programmer’s problem. If you want to do high performance numerical work, OpenMP is a nicer environment than POSIX threads. If you want to speed up COBOL, it looks like being the other way around. 5 Above the language Imagine a computer system serving thousands of concurrent users. A user fills out a form on a smart terminal; the request is sent to the server; a new thread is created; component code will be loaded if not already in memory; the thread will check the request, pull in data from files and data bases; it may update files and data bases; and when the thread sends the result back, it terminates. Some time later its code may be removed from memory. Threads may start other threads. File accesses may be locked. The file structures supported are rather like those offered by the Berkeley Data Base library. The system as a whole provides transaction support, so that if a thread crashes, all its changes to persistent storage are undone. While multiple threads are used, the software runs as a single operating-system process. I’ve just described a Web server like Apache, haven’t I? Well, no. I’ve described IBM’s Customer Information Control System [13,14]. It’s a 43-year-old framework for transaction processing. In 1992, components could be written in assembler, PL/I, or COBOL. The screens and keyboards belonged to IBM 3270-family terminals, not PCs running a web browser, but when I learned about CGI and servelet programming, I said “What has been will be again, what has been done will be done again; there is nothing new under the sun.4 Except this time without automatic transaction rollback on failure.5” When people talk about so many financial transactions going through COBOL, most of that COBOL code is running under CICS. This is by any standards6 a spectacularly successful framework. What was done to fit COBOL into this framework? • A preprocessor takes a COBOL source file with “EXEC CICS” extensions and generates plain COBOL with extra declarations and calls to library routines. • COBOL file management statements were replaced by special CICS file statements. 4Ecclesiastes 1:9, NIV translation 5“Hegel remarks somewhere that all great world-historic facts and personages appear, so to speak, twice. He forgot to add: the first time as tragedy, the second time as farce.” — Karl Marx 6other than æsthetic 12
  13. • Statements for managing screens (very roughly analogous to providing

    some level of HTML support, modern versions of CICS do work with web browsers) were added. • Statements for calling other programs were added. • Statements for sending and receiving messages through queues were added. (Modern CICS interfaces with the IBM Websphere MQ message queue system.) • Statements for transaction management and logging were added. The result of preprocessing was plain COBOL. COBOL is a syntactically complex7 language, but COBOL 85 was for the most part semantically simple, so that a compiler could do good data flow analysis without worrying about pointers and aliasing. The compiler did not need to do new kinds of analysis, but generated code must address code and global variables via an offset from a base register rather than using absolute addresses. This is established technology, so COBOL systems can exploit multicore without language changes. It is impossible for a COBOL 85 program to create pointers, and array subscripts are commonly checked, so it is practical to have multiple COBOL components running concurrently in a single address space. Because COBOL 85 did not allow recursion, it was possible for a COBOL compiler to determine how much memory a component would need, so it was not necessary to worry about stack overflow or stack shifting. Because pointers could not be passed in messages, there was no need to worry about objects allocated in one component and freed in another.8 (Think of server-side applets hosted in a “safe” program- ming language.) Such a design naturally exploits multiple cores, without the components themselves having to do so. One of the reviewers commented that “almost all COBOL is I/O bound”. Arguably, that’s precisely the problem that CICS solves. (Just as chip-level multithreading addresses the problem of CPUs waiting for memory.) One trans- action may be waiting for I/O; very well, run another one. Mainframe systems tend to have many discs, and RAID is established in commodity systems. There are other main-frame manufacturers who offer transaction processing systems. If you asked J. Paul Morrison, “how do you put concurrency into a COBOL system”, I think he’d say “don’t take it out in the first place.” His approach, called Flow-Based Programming[8], is about as old as CICS. Starting in 1969, Morrison built a system called “the Advanced Modular Processing System”. The key ideas were disclosed in a 1971 Technical Disclosure Bulletin. Towards the end of the 80s, Morison and Wayne Stevens built a new version of AMPS called “the Data Flow Development Manager”, used for a 7or “bloated” 8Of course it is impossible to persuade most programmers that the limitations of a language are one of its most valuable gifts, so dynamic memory allocation was bodged in after all. But uncontrolled sharing was kept out. 13
  14. number of projects in IBM Canada and marketed by IBM

    Japan. Since 1993 there has been a C version called THREADS — “THREads-based Application Development System”. There are also implementations in Java, C#, Python, Javascript, and PHP. The core idea of Morrison’s Flow-Based Programming is to build a system in two layers: components are written in the procedural language of your choice, and hooked up by a concurrent coordination language. UNIX programmers are already familiar with the idea of filters written in C hooked up by scripts written in ksh or bash. Flow-Based Programmer starts with that and takes it much further. Components simply read streams and write to streams and are not directly concerned with concurrency. They don’t share memory, so they may be dis- tributed over many machines or they may run as threads in a single operating system process. Provided that some sequential “files” can actually be flow-based streams, COBOL is fine for writing FBP components. No changes to language or compiler are needed, although once again dynamically linkable position-independent code is helpful. Some of the flow-based systems available on the web work within a single language and do not exploit the component language/coordination language dichotomy. This misses the opportunity to analyse systems at the coordination level, which seems to me a potential strength of this approach. 6 Recommendations There is no one approach that is best for all purposes. The above-the-language approach is proven technology which has been suc- cessfully imitated in other languages, but it does nothing for parallelism within a component. The below-the-language approach has a strength and a weakness and they are the same thing: the programmer-visible language semantics do not change. The strength is that the programmer does not get to make new kinds of mis- takes; the weakness is that the programmer does not get to use concurrency or parallelism as a structuring tool. The within-the-language approach is the most flexible, but requires a two- fold investment: new programs to exploit the new features and revised compilers to implement them. This may well be an idea whose time has gone, because the very simplicity of COBOL that made Concurrent COBOL attractive (by making good safety analysis straightforward) has been largely destroyed in the 2002 standard and later revisions. COBOL 85 makes a better starting point. 14
  15. 7 References • COBOL standards: ISO 1989:1985, Programming languages —

    COBOL. ISO/IEC 1983/AMD1:1992, Programming languages — COBOL: Intrinsic function module. ISO/IEC 1989/AMD2: Programming languages — Correction Amendment for COBOL. ISO/IEC 1989:2002, Programming languages — COBOL. ISO/IEC 1989:2002/Cor 1:2006 ISO/IEC 1989:2002/Cor 2:2006 ISO/IEC 1989:2002/Cor 3:2009. ISO/IEC TR 19755:2002, Information technology — Programming lan- guages, their environments and system software interfaces — Object fi- nalization for programming language COBOL. ISO/IEC TR 24716:2007, Information technology — Programming lan- guages, their environments and system software interfaces — Native COBOL Syntax for XML Support. ISO/IEC TR 24717:2009, Information technology — Programming lan- guages, their environments and system software interfaces — Collection classes for programming language COBOL. 2 POSIX: IEEE Std 1003.1TM 2008. http://pubs.opengroup.org/onlinepubs/ 9699919799/. 3 OpenMP: “The OpenMP R API Specification for Parallel Programming”. www.openmp.org. 4 OpenMP Application Program Interface Version 3.1. July 2011. http: //www.openmp.org/mp-documents/OpenMP3.1.pdf 5 OpenMP book: “Using OpenMP, Portable Shared Memory Parallel Pro- gramming’, Chapman, B., Jost, G., and van der Pas, R. MIT Press 2007. ISBN 9780262533027. 6 COBOL/OpenMP patent: “US Patent Application 20110093837 - Method and apparatus for enabling parallel processing during execution of a cobol source program using two-stage compilation.” Bull HN Information Sys- tems Inc. http://www.patentstorm.us/applications/20110093837/ description.html Filed October 20, 2009. Published April 21, 2011. 7 Erlang book: “Programming Erlang: Software for a Concurrent World”, Joe Armstrong, The Pragmatic Programmers, 2007. http://pragprog. com/book/jaerlang/programming-erlang. 8 “Flow-Based Programming”, J. Paul Morrison, van Nostraind Reinhold, 1994. Republished on the Web in 2009. Start at http://en.wikipedia. org/wiki/Flow-based_programming. 15
  16. 9 “Notes on Programming Language Design”, C. A. R. Hoare,

    Stanford AI Memo 224, report STAN-CS-73-403, 1973. 10 Henry Ford Community College CIS Department FAQ entry for COBOL. http://cis.hfcc.edu/faq/cobol 11 “Autovectorization with LLVM”, Hal Finkel, The LLVM Compiler Infras- tructure 2012 European Conference, 2012. http://llvm.org/devmtg/ 2012-04-12/Slides/Hal_Finkel.pdf 12 “Berkeley DB”, entry revised December 2012. http://en.wikipedia. org/wiki/Berkeley_DB]\item13] “CICS for the COBOL Programmer — Part 1: An introductory course”, 2nd edition, by Doug Lowe. Mike Mu- rach & Associates Inc, 1992. ISBN 0-911625-60-7. 14 “CICS for the COBOL Programmer — Part 2: An advanced course”, 2nd edition, by Doug Lowe. Mike Murach & Associates Inc, 1992. ISBN 0- 911625-67-4. Perhaps not the best CICS references, and way out of date, but what I learned CICS from. 16