Wednesday, November 25, 2020

Ideas for Windows

Where should Windows go? What improvements should Microsoft make?

I have some ideas.

First, Microsoft should drop the '10' designation of Windows. Yes, Windows 10 is different from Windows 8.1 and Windows 8 (and Windows 7, and Windows Vista, and...) but that's not important. What's important is how people talk about Windows, and how Microsoft wants people to talk about Windows.

People don't talk about "Windows 10"; they call it "Windows 1809" or "Windows 20H2". And those names are good enough to identify a version of Windows. The "10" is superfluous.

There is another benefit to dropping the '10'. It means that Windows doesn't get embroiled in marketing battles with Apple's macOS. Apple just updated macOS from version 10 to version 11. Is Apple now "ahead" in the operating system race because they have version 11? Is Microsoft behind because they have version 10? (Fans of the old movie "This is Spinal Tap" can make jokes about "our amplifiers go up to 11".)

By dropping the '10' and referring to release numbers, Microsoft avoids all of the comparisons. Or at least the meaningless comparisons of '10' and '11'.

Looking beyond version numbers, Microsoft is moving into a position which will let it reduce the number of configurations for Windows.

Today, Windows has multiple families: "Windows Server", "Windows Professional", and "Windows Home".  When Windows was sold in a box, these divisions made sense.

As Windows moves to a subscription service, the notion of different Windows versions makes less sense. The subscription model changes the idea of the purchase. Instead of "I'm buying this Windows product today and I will use it for a few years, getting free updates in that time" the idea becomes "I have subscribed to Windows, I pay a monthly fee, and this month I want this configuration of Windows". The difference is that one can change the product during the subscription (much like changing a Netflix account to allow more or fewer screens at the same time).

With a Windows subscription, one could start with Windows Home, then "dial up" the configuration to Windows Professional for a few months.

Maybe the designations of "Home" and "Professional" don't make sense anymore. Perhaps a subscription to Windows gives one access to a set of features (more granular that "Home" or "Professional"), and you can activate (or deactivate) new features as you want. Instead of choosing one of three options, one chooses from a large menu of services, enabling those that make sense.

I think we will see a number of changes as Microsoft moves from the "sale in a box" concept to the subscription concept of software.

Tuesday, November 17, 2020

The joy of a small programming language

I recently had the pleasure of reading the reference manual for Dibol (a programming language from the 1970s). And reading the manual was a pleasure. What made it so was the brevity and directness of the manual, the simplicity of the concepts, and the feeling of confidence in my ability to start programming it the language.

Dibol is a language from the minicomputer era, a short period of computing that occurred prior to the PC revolution. Made by DEC, it was an alternative for COBOL that ran on small systems and operated on simple files. It's syntax was influenced by COBOL, but much simpler.

The entire reference manual ran about 30 pages.

Dibol, I think, would have fit well in the Unix environment. Unix systems tended to use text files with flexible formats and varying line lengths, and Dibol was built for text files with fixed-length formats, so the match is not perfect. But Dibol is a language that follows the Unix philosophy: a simple tool to perform a simple task.

I could see it as a companion to AWK, a small language which handles text files and variable-length lines.

Dibol didn't "make it" as a programming language. It lives today, in the form of "Synergy/DE" which is a large superset of the original Dibol. So large, in fact, that it perhaps no longer follows the Unix idea of a simple tool. (I suspect the reference manual is longer than 30 pages.) But Dibol and its successors DBL and Synergy/DE have no significant presence in the popularity lists from Tiobe or PYPL or RedMonk.

We have no small languages today -- at least no small languages that enjoy any amount of popularity. AWK may be the most famous of the small languages, and even it is in the lower ranks on popularity lists. The popular languages are big (C++, Java, Python, C#, R, Visual Basic...).

We should be careful with the popularity indices. The methods used by them may skew the results. Tiobe and PYPL count queries about the languages -- people asking questions. I think we can safely assume that people will ask more questions about large languages than small languages, so complex languages have an advantage in the popularity metrics.

RedMonk also looks at Github and the code posted there. It's not clear if RedMonk is counting number of projects, number of files, or lines of code. (A complex language would probably have more lines of code.) But Dibol and other small languages do not show in RedMonk's list.

So we can conclude that there is no significant use of small programming languages. As a programming language, you're either big or you're unknown.

Which, I think, is a shame. The big language are hard to learn and easy to create large, complicated systems. Small languages have the advantage of doing one job and doing it well. We as an industry may be missing out on a lot.

It also means that large programming languages are the "go to" solution for programmers; that programmers prefer large languages. There is some sense in this. A large programming language (C#, Java, take your pick) can handle just about any task. Therefore, a programmer who knows a large programming language is capable (in one sense) of handling any task. Large programming languages give the programmer flexibility, and more opportunity. (I say "in one sense" because knowledge of a programming language is, while necessary, not sufficient for the design of large or specialized applications. System architecture, user requirements, performance analysis, and deployment practices are also required.)

The consistent use of large programming languages means that our solutions will tend to be large. I know of no small Java applications, no small C++ applications, and no small C# applications. There may be small utility programs written in large languages, but I suspect that those utility programs grow over time and become large utility programs.

Large code bases require more effort to maintain than small code bases, so our bias towards large programming languages is, in the long run, costing our customers time and money.

Of course, the alternative (lots of small programs written in small programming languages) can have the problem of multiple languages and not enough people to maintain them. A shop with hundreds of programs written in a dozen or so different programming languages also faces maintenance efforts. Should every programmer in the shop know every programming language used in that shop? That also requires time and effort.

So what should a programmer do? Learn one large language? Learn lots of small languages?

My recommendation is to learn multiple languages, some large and some small. For large languages, pick two of the popular languages C, C++, C#, Java, JavaScript, and Python. Also learn some small languages such as Awk, Lua, and bash.

But don't spend time on Dibol.

Wednesday, November 11, 2020

Flipping bits

Apple has introduced a new line of Mac computers which use Apple's new M1 system-on-a-chip. Apple made several claims about the performance, but those claims were vague -- at least in terms of measured performance. (Claims were in the form of "up to 2 times faster", "up to five times faster", "the fastest Macbook yet".)

Apple's M1 chip contains a processor that is based on ARM architecture, which is quite different from Intel's x86 architecture. How does one compare them?

One of the oldest comparisons of processors is MIPS (millions of instructions per second). Dating back to the mainframe age, it is simple in concept: count the number of instructions in each second of processing. But when comparing different architectures, a comparison of instruction to instruction is not useful. The instructions sets are different, and a computation on one processor might use one instruction, and the same computation on a different processor might take three or four instructions.

A later metric, FLOPS (floating-point operations per second) is somewhat better when the two systems are performing tasks that use a lot of numeric processing. This metric focusses on the instructions used for processing numeric values, specifically floating-point values, and looks at operations and not necessarily discrete instructions. But its focus is its weakness: Tasks that use integer arithmetic and fixed-point data representations aren't usefully measured with FLOPS metrics.

Thinking about the differences between Intel and ARM processors, perhaps we need a different metric, one that is robust for different architectures and different types of processing. And I have thought of one, although it may not be practical.

My idea for measuring performance is to execute a task or function and count the number of bits that are changed (or "flipped") during the process.

The advantage of this metric is that it is neutral to architectures and also neutral to the type of processing.  MIPS doesn't measure different instruction sets well and FLOPS omits integer operations. Bit-flipping, in contrast, works with all architectures and all data types. (I'll get to the downside of the bit-flipping metric later.)

The idea of bit flipping is simple: count each bit that changes state, either from 0 to 1 or from 1 to 0. If a register holds 0x0001 and is cleared, then one bit is changed (from 1 to 0). If a register holds 0x001f and is cleared, then five bits are changed. (If a register holds 0x0000 and it is cleared, then no bits are changed.)

Such a measure has the advantage of working with any type of data. We can use this measure with floating-point data and with integer data. We can even use it with BCD (binary converted decimal) data. We can use it with text data and with objects.

Notice that this measure does not measure time. A comparison of the number of bits changed during a process is a count, not a time. One processor might be flipping more bits but running faster. A higher flip count does not necessarily mean slower performance.

One might think that such a limitation makes the bit-flip metric useless for performance comparisons. If one is interested in speed, and only speed, then the point is valid. Bit-flipping measures the efficiency of the processor: the same task with a lower bit-flip count is more efficient -- and therefore uses less energy. That may be useful to system designers.

One challenge with the bit-flip metric is deciding which bits count. Even though bits are all the same (a single value of either 1 or 0) different bits are stored in different locations and used for different purposes.

One simple difference is memory and registers. Processors store values in memory ("RAM") and also store values in registers, a small set of locations that are part of the processor. Registers are used for computations and change often; memory is used for storage and changes less frequently. Should the bit-flips for memory count the same as bit-flips in registers?

If we agree to count registers and memory differently, then we open the door to other distinctions in bits. Modern processors have caches, usually three layers, which store values too. Caches exist between registers and memory. If registers and memory have different "weights" for bit-flipping, caches should probably have different weight too.

We've been looking at data, but processors also use addresses. In order to read or write a value in memory, the processor must put an address on the buss to tell memory which cell it wants. Those bits change; should we count those changes too?

Addresses have more complications. Modern processors use an advanced form of memory management, which allows for multiple processes. Memory management changes the address that the process requests (a "logical" address) to the real address that holds the value (the "physical" address). Should those bit-flips count? (Keep in mind that physical memory pages are assigned at random, so two separate runs could have different physical pages and therefore different memory addresses and therefore different bit-flip counts.)

After careful consideration, I think the best solution is to count all of the bits. ("All the bits!") Categorize them if you want, report them in groups if you want, assign different weights to the different groups if you want, but count all of them.

We could compare similar tasks on Windows and Mac, on Intel and ARM, even Microsoft Word and Office Libre.

Which brings me to the one drawback to bit-flip counts.

It's hard to do.

Counting bit-flips requires access to memory, registers, and all sorts of innards of the processor. It's not something an application can do, or even a device driver or kernel extension. This kind of observation must occur in the processor; nothing else will do.

To effectively use bit-flipping, we need the cooperation of Intel and ARM (and Apple, who designs the M1 off of the ARM design). We also need clear and precise descriptions of the data we want to collect, so that the counts on Intel processors count the same things as counts on ARM processors.

We also need an answer to the meta-question about data collection: Counting bit-flips in itself changes data values (the counters for bit-flip operations). Do we count those bit-flips too? (For the record, I am against counting those bit flips. Because if we count the bit-flip counter flips, then we have to count the counters for those bit flips, and the count the bit flips for those counters, and on and on. Enough is enough!)

Bit-flipping is a nice thought experiment. Implementation requires changes to processors and the cooperation of manufacturers, which is a tall order. I suspect it will remain a thought experiment.

Unless!

Unless... we limit bit-flips to something simple (such as only the CPU registers, and nothing else) and we leverage virtual modes, letting the "real" processor count the bit flips in the "virtual" processor. (Bit-flipping is a simple operation: XOR the "before" and "after" values, and then count the bits in the result.)

Such an arrangement might just be possible. It still needs cooperation from the processor designers, but such a scaled-down version might be possible.

Thursday, November 5, 2020

Big is the new big

For PCs, big is the new big. It wasn't always this way.

When the PC revolution started, small was big. The advantage of PCs was smallness, in size, in cost, and in effort. PCs were small enough to fit on a desktop, cheap enough to be purchased as "word processors" (to avoid the data processing bureaucracy), and easy enough that one person could operate them.

The programmers of the PC revolution took pride in the smallness of hardware and of programming languages. BASIC, the original language of the PC revolution, could fit on a computer with 8K of RAM. (That's 8192 bytes.) Other programming languages were also small. Even the names emphasized smallness: Tiny-C, Tiny-Pascal.

We rebels of IT considered "big" an aspect of mainframe systems, and associated "big" with slow, lumbering processes that hindered productivity. The Agile movement was defined as the opposite of "BDUF" (Big Design Up Front). Big was what we did not want our systems to be.

Yet something has changed. We no longer want to be small. Now, we want to be big.

Notice the (now decade-old) desire for "big data". That was a full embrace of bigness. There was also "big analytics" which analyzes large datasets.

Other things have become big. Databases have become big. Spreadsheets have become big. Word processors have become big. Our PCs are not stand-alone systems, but nodes in a larger system and reliant on that system for authentication, updates, and basic processing.

Programming languages have become big, with C++, Java, Python, C#, and Visual Basic in the top slots of the Tiobe index for October 2020. The top position is held by C. While some consider C a small programming language, I have my doubts. C uses a small set of keywords, but has a large standard library. It may not be large, but it isn't small.

We have lost our desire for small systems. Our smart phones are physically small, yet their operating systems are complex and app development is for professionals. Even small hardware systems such as the Raspberry Pi run a full Linux distro, which requires a fair amount of administration.

The only small systems are the Arduino and similar systems, which run a slim monitor operating system and are designed for hardware control, not general-purpose computing.

Perhaps we were deluding ourselves. Perhaps we wanted large systems all along. We craved the power of fast processors, ample memory, and disk space that was not limiting -- that much is certain. Perhaps the cost of faster processors, more memory, large disk space, and interconnected systems is bigness. Perhaps it is not possible (or not cost-effective) to operate small systems loosely coupled.

Perhaps.

But perhaps the desire for small, simple systems remains, and perhaps, one day, it will resurface.


Wednesday, October 7, 2020

Platforms are not neutral

We like to think that our platforms (processor, operating system and operating environment) are neutral, at least when it comes to programming languages. After all, why should a processor care if the code that it executes (machine code) was generated by a compiler for C#, or Java, or Fortran, or Cobol? Ditto for the operating system. Does Windows care of the code was from a C++ program? Does Linux care of the code came from Go?

And if processors and operating systems don't care about code, why should a platform such as .NET or the JVM care about code?

One could argue that the JVM was designed for Java, and that it has the data types and operations that are needed for Java programs and not other languages. That argument is correct: the JVM was built for Java. Yet people have built compilers that convert other languages to the JVM bytecode. That list includes Clojure, Lisp, Kotlin, Groovy, JRuby, and Jython. All of those languages run on the JVM. All of those languages use the same data types as Java.

The argument for .NET is somewhat different. The .NET platform was designed for multiple languages. When announced, Microsoft supplied not only the C# compiler but also compilers for C++, VB, and Visual J++. Other companies have added compilers for many other languages.

But those experiences do not mean that the platforms are unbiased.

The .NET platform, and specifically the Common Language Runtime (CLR) was about interoperability. The goal was to allow programs written in different languages to work together. For example, to call a function in Visual Basic from a function in C++.

To achieve this interoperability, the CLR requires languages to use a common set of data types. These common types include 32-bit integers, 64-bit floating-point numbers, and strings. Prior to .NET, the different language compilers from Microsoft all had different ideas about numeric types and string types. C and C++ user null-terminated strings, but Visual Basic used counter-in-front strings. (Thus, a string in Visual Basic could contain NUL characters, but a string in C or C++ could not.) There were differences with floating-point numeric values also.

Notice that these types are aligned with the C data types. The CLR, and the agreement on data types, works for languages that use data types that match C's data types. The .NET version of Visual Basic (VB.NET) had to change its data types in order to comply with the rules of the CLR. Thus, VB.NET was quite a bit different from the previous Visual Basic.

The CLR works for languages that use C-style data types. The CLR supports custom data types, which is nice, and necessary for languages that do not use C-style data types, but then one loses interoperability, and interoperability was the major benefit of .NET.

The .NET platform favors C-style data types. (Namely integers, floating point, and NUL-terminated strings.)

The JVM also favors C-style data types.

Many languages use C-style data types.

What languages don't use C-style data types?

Cobol, for one. Cobol was developed prior to C, and it has its own ideas about data. It allows numeric values with PICTURE clauses, which can define limits and also formatting. Some examples:

   05  AMOUNT1          PIC 999.99.
   05  AMOUNT2          PIC 99999.99.
   05  AMOUNT3          PIC 999999.99.
   05  AMOUNT4          PIC 99.99.

(The '05' at the front of each line is not a part of the variable, but indicates how the variable is part of a larger structure.)

These four different values are numeric, but they do not align well with any of the CLR types. Thus, they cannot be exchanged with other programs in .NET.

There are compilers for Cobol that emit .NET modules. I don't know how they work, but I suspect that they either use custom types (which are not easily exchanged with modules from other languages) or they convert the Cobol-style data to a C-style value (which would incur a performance penalty).

Pascal has a similar problem with data types. Strings in Pascal are length-count strings, not NUL-terminated strings. Pascal has "sets" which can contain a set of values. The notion of a set translates poorly to other languages. C, C++, Java, and C# can use enums to do some of the work, but sets in Pascal are not quite enums.

Pascal also has definite ideas about memory management and pointers, and those ideas do not quite align with memory management in C (or .NET). With care, one can make it work, but Pascal is not a native .NET language any more than Cobol.

Fortran is another language that predates the .NET platform, and doesn't work well on it. Fortran is a simpler language that Cobol or Pascal, and concentrates on numeric values. The numeric types can convert to the CLR numeric types, so compiling and exchanging data is possible.

Fortran's strength was speed. It was (and still is) one of the fastest languages for numeric processing. Its speed is due to its static memory layout, something that I have not seen in compilers for Fortran to .NET modules. Thus, Fortran on .NET loses its advantage. Fortran on .NET is not fast, and I fear it never will be.

Processors, too, are biased. Intel processors handle binary numeric values for integers and floating-point values, but not BCD values. IBM S/360 processors (and their descendants) can handle BCD data. (BCD data is useful for financial transactions because it avoids many issues with floating-point representations.)

Our platforms are biased. We often don't see that bias, most likely because with use only a single platform. (A single processor type, a single operating system, a single programming language.) The JVM and .NET platforms are biased towards C-style data types.

There are different approaches to data and to computation, and we're limiting ourselves by limiting our expertise. I suspect that in the future, developers will rediscover the utility of data types that are not C-style types, especially the PICTURE-specified numeric types of Cobol. As C and its descendants are ill-equipped to handle such data, we will see new languages with the new (old) data types.


Wednesday, September 30, 2020

The future of Firefox

Use of Mozilla's Firefox browser is declining (at least as a percentage of market share), and people are concerned.

Some are concerned that we will lose an option in the browser market. Others are concerned that the demise of Firefox signals a forthcoming decline of open-source software. Mozilla, of course, is concerned about its business.

I'm not sure what Mozilla should do in this situation. I do have some observations:

First, people select browsers (and other things) for one of two reasons.

1) They want to use the specific product (in this case, the Firefox browser)

2) They don't want to use the alternatives (in this case, Internet Explorer, Edge, Chrome, Safari, etc.)

To improve its market share, Mozilla will have to either provide a product or service that people want to use, or be an alternative to a product that people don't want to use. Mozilla must either make a better browser, one that people look at and think to themselves "yeah!", or wait for people to dislike the other browsers on the market.

When Chrome appeared on the market, people used it, I think, for the latter reason. At the time, Internet Explorer (IE) was the most commonly used browser (sometimes by corporate dictat) and people did not like it. Chrome was not Internet Explorer, and by using Chrome, one could "poke Microsoft in the eye".

But that was then. Now, people use Chrome because they want to. People might have chosen Chrome after using Gmail, and may have had favorable opinions of Google due to the 1GB space for mailboxes, which was quite large at the time. And Gmail was free!

Whatever the reasons, people like Chrome. Mozilla does not have the tailwind of people disliking their current browser.

Waiting for potential customers to dislike their current product is not a viable strategy. People may be unhappy with some of Google's practices, and that may drive some away from Chrome (and some of those to Firefox) and Mozilla has been advertising along those lines.

But dislike of Google is probably not enough. Mozilla needs "a better mousetrap".

And I'm not sure how they can build one.

Wednesday, September 16, 2020

Technical debt is similar to new features

If we accept the premise that technical debt is the difference between what we want the code to be and what the code actually is, we can treat technical debt like new requirements.

Most development shops have a process for new requirements. That process usually involves describing the new feature, estimating the benefits (additional revenue, reduced expenses), estimating the effort, scheduling the work, testing, and deploying the new requirements in a new version.

An organization can use a similar process for technical debt. Technical debt must be described ("module X fails to meet code style rules 12, 14, and 17" or "library Y is obsolete"), estimated for benefits, estimated for effort, scheduled, tested, and deployed.

Technical debt does vary from new requirements in two ways. First, new features are often about revenue or cost reduction, and technical debt is about the reduction of risk. This difference often makes new requirements more desirable; revenue is often better than less risk, especially for a working system.

The second difference is the sponsors for changes. New features often have organizational sponsors. They may have titles such as "product owner", "VP of marketing", or "user representative". These sponsors make the case for the new feature, presenting the benefits to the managers who control the project budget.

In contrast, the sponsors for technical debt are (almost always) developers. It is the developers who see the problems in the code, the developers who want to improve things.

But "improving the code" is not a goal that makes sense for the business. Managers are concerned with four general topics: money, time, risk, and politics. "Improving the code" is not one of them. To get managers to "buy in" to the effort to reduce technical debt, developers must make the case in management terms. They have to convert "improvement to the code" into one of money, time, risk, and politics.

A change to eliminate technical debt may, on occasion, offer a reduction in cost, but usually the benefit is a reduction in risk. To get time and resources to reduce technical debt, developers must present a clear case about the risk caused by the current code, and possible costs (money, time, delayed releases) in the future.

If developers can make their case, then the process for technical debt is quite similar to the process for a new feature. Changes for technical debt can be identified, described, tracked, prioritized, estimated, scheduled, implemented, and deployed.

Technical debt is not a mysterious aspect of software development. It can be described and measured, and the effort to reduce it can be estimated, prioritized, scheduled, and implemented -- just like the efforts for  new features. The big difference between technical debt and new features is that technical debt is about risk, and new features are about money. Any organization must balance the needs of money, time, risk, and politics -- that's management.