## Solution to X-Toolkit and portability.

Heater
Posts: 14220
Joined: Tue Jul 17, 2012 3:02 pm

### Re: Solution to X-Toolkit and portability.

ejolson,

That's an interesting example and obviously a dramatic speed up.

Sadly it did not answer my question as you don't present the source of bot the optimized and unoptimized versions. I would expect some effort to be put into optimizing a maths library that is intended to be use by many.

Mind you one can see a factor of 60 in speed between cache friendly and cache unfriendly code without much effort. That's before we start thinking of using NEON or whatever.

Let's look at a simpler example. Say we want to visit every element of a big twp dimensional array. Like so:

Code: Select all

``````#define SIZE 10000
int bigArray[SIZE][SIZE];

void function ()
{
for (int i = 0; i < SIZE; i++)
{
for (int j = 0; j < SIZE; j++)
{
bigArray[i][j] = 1;
}
}
}
``````
This is pretty fast as it stands. But if we happened to be unaware of cache issues we might traverse the array in a cache unfriendly way. Like so:

Code: Select all

``````void function ()
{
for (int i = 0; i < SIZE; i++)
{
for (int j = 0; j < SIZE; j++)
{
bigArray[j][i] = 1;
}
}
}
``````
BOOM you are now 10 or so times slower!

One could try to be clever and optimize your code though. Let's get rid of one loop and remove all that array indexing by visiting the elements through a pointer. Like so:

Code: Select all

``````void function3 ()
{
int* p = (int*)bigArray;
for (int i = 0; i < (SIZE * SIZE); i++) {
*p++ = 1;
}
}
``````
That's nice, we have a speed up of 1.5. BUT, the cost of doing that is obfuscating the intention of the code and making it harder to understand. Is it really worth a 1.5 times speed up?

But wait, forgot to turn compiler optimization on. Let's use -03.

Wow, what do we find? Firstly we have a 60 times speed up over the cache unfriendly version compiled at -O0. That's great. BUT our obscured "optimized" single loop version is exactly the same speed as the original double loop version.

Conclusion: Dicking around trying micro-optimize code is a waste of time, unless you really need to for some reason, and risks obfuscating the source unnecessarily

Here are the timings (microseconds) I got:

At -O0

\$ g++ -O0 -Wall -o bigarray -std=c++11 bigarray.cpp
\$ ./bigarray
function1: 13294683
function2: 1597411
function3: 1004497

At -O3

\$ g++ -O3 -Wall -o bigarray -std=c++11 bigarray.cpp
\$ ./bigarray
function1: 13042701
function2: 226281
function3: 226305

Interesting thing here is that switching on optimization does not help our bad cache-unfriendly code at all (function1). And it renders our efforts at optimizing away the loop (function3) pointless.
Last edited by Heater on Sat Nov 19, 2016 5:24 am, edited 1 time in total.
Memory in C++ is a leaky abstraction .

DavidS
Posts: 4334
Joined: Thu Dec 15, 2011 6:39 am
Location: USA
Contact: Website

### Re: Solution to X-Toolkit and portability.

It might be easy for you to read, but anybody else has to take great care and probably do a lot of looking back and forth whilst trying to figure out what everything means. If the names are descriptive then you don't need to keep searching back to see what was meant.
I must disagree. And it seems that you forgot the purpose of comments?

The comments are as important as the direct code for reading code. Even if someone else had written it the "GOOD WAY" example would be way easier for me to read and understand, even if I had not the detailed knowledge of RISC OS (which is the target of the example). It is far easier to quickly see the structure without all that extra text intermingled with the code, and the comments off to the side give the needed detail to fully understand what is going on.

Yes I forgot the char type for the element named "String" in the poor example.
RPi = The best ARM based RISC OS computer around
More than 95% of posts made from RISC OS on RPi 1B/1B+ computers. Most of the rest from RISC OS on RPi 2B/3B/3B+ computers

Heater
Posts: 14220
Joined: Tue Jul 17, 2012 3:02 pm

### Re: Solution to X-Toolkit and portability.

DavidS,

I'm not sure what your example has to do with "modern standards". Short, cryptic, meaningless variable names have been frowned upon since I started programming in the mid 1970's. Your code would have failed review at most places I have worked at.

Short names are OK if they are confined to some limited context where they make sense. For example if I have a simple function called "countElements" I would not like to see a local counter variable in there called "elementCount",why not just call it "count"? The context is obvious.

On the other hand calling the function "cntElms" and the counter "elmCnt" would get you fired.

Comments are great and all but using meaningless names then explaining them in a comment is just silly. For example:

long x0,y0; //The minimum (x,y) bounds.

Fine, but now all throughout your code you will use x0 and y0 and at that point the reader gets no hint or reminder what they are. Or are you going to now add a comment to say "minimum (x,y) bounds" at every point of usage?

Better would be something like:

// Bounding box
long x0, y0, x1, y1;

By the way, I don't like to see that union in there:

union { //Icon Data.
char t[12]; //If a string.
long i[3]; //If inderected.
}dta;

That kind of thing is not portable, could have endianness issues.
Memory in C++ is a leaky abstraction .

ejolson
Posts: 4019
Joined: Tue Mar 18, 2014 11:47 am

### Re: Solution to X-Toolkit and portability.

Heater wrote: long i[3]; //If inderected.
I searched on "inderected" and couldn't find a meaning. Is this a misspelling? Also, how would the union be affected by endianness? Is it being written as one type and read as the other?
Heater wrote:Sadly it did not answer my question as you don't present the source of bot the optimized and unoptimized versions.
I only presented the source for the code I wrote. OpenBLAS is available online.
Last edited by ejolson on Sat Nov 19, 2016 6:59 am, edited 1 time in total.

DavidS
Posts: 4334
Joined: Thu Dec 15, 2011 6:39 am
Location: USA
Contact: Website

### Re: Solution to X-Toolkit and portability.

@Heater:
I agree that meaningless names are useless.

You will note that every veriable name I used has a good meaning. It is obvious when they are being used what they are being used for, thus it is obvious that Icn describes an Icon definition, and that ICON is a type for an icon definition, and that x0 and y0 are minimal bounds along x and y, same is true for the rest.

I chose very descriptive names that are short and to the point, so they do not get in the way of reading the code.

Now the same could have been done just as readable using Icon as the variable instance for the ICON, and using Data instead of Dta, and txt instead of t, though where it is used these changes would not improve readability, though also would not have detracted from readability. So for the variable names, and the names for the elements of the struct it is 100% readable without the comments, that I made certain of.

And as far as using defined names for bits in a flag word goes, you end up have to look at the header to see what the bits are, where if you just put the value in place and comment it you have the information where it is being used. So if you want readability DO NOT use defined constants, it just makes it a lot less readable. I get tired of having to have between 1 and 5 header files open just to figure out what the constants are supposed to be, this is a large part of the reason I do not use as much code by others as I would like to.

I believe in readable code, not overly verbose code that makes itself less readable. If you really prefer that first example (the "Modern Standard" example) you may as well write obfuscated code without comments, it would at least be more readable for a human.

In this case "Modern Standard" refers to the standards that people try to push for profesional use, that make things worse.
RPi = The best ARM based RISC OS computer around
More than 95% of posts made from RISC OS on RPi 1B/1B+ computers. Most of the rest from RISC OS on RPi 2B/3B/3B+ computers

DavidS
Posts: 4334
Joined: Thu Dec 15, 2011 6:39 am
Location: USA
Contact: Website

### Re: Solution to X-Toolkit and portability.

ejolson wrote:
Heater wrote: long i[3]; //If inderected.
I searched on "inderected" and couldn't find a meaning. Is this a misspelling? Also, how would the union be affected by endianness? Is it being written as one type and read as the other?
My spelling, thank you for pointing it out. I do attempt to spell correctly, though often I make mistakes.

As to endianess, this example targets RISC OS, so there is only one possible endianess.
RPi = The best ARM based RISC OS computer around
More than 95% of posts made from RISC OS on RPi 1B/1B+ computers. Most of the rest from RISC OS on RPi 2B/3B/3B+ computers

PeterO
Posts: 5315
Joined: Sun Jul 22, 2012 4:14 pm

### Re: Solution to X-Toolkit and portability.

Heater wrote:
Interesting thing here is that switching on optimization does not help our bad cache-unfriendly code at all (function1). And it renders our efforts at optimizing away the loop (function3) pointless.
Which illustrates my point nicely !
PeterO
Discoverer of the PI2 XENON DEATH FLASH!
Interests: C,Python,PIC,Electronics,Ham Radio (G0DZB),1960s British Computers.
"The primary requirement (as we've always seen in your examples) is that the code is readable. " Dougie Lawson

Heater
Posts: 14220
Joined: Tue Jul 17, 2012 3:02 pm

### Re: Solution to X-Toolkit and portability.

I thnk 99 prcnt of prgrmmrs wld sy tht rmving vwls frm wrds to mk vrble nms is unncssry nd mks lgblty hrdr.

I liked x0, y0, x1, y1.
So if you want readability DO NOT use defined constants

If you have some constant that is reused many times in your program, perhaps in many files, then when you want to change it you will have to find it all those files and change it in many places.

It also means that if you make a mistake, a typo or just selecting the wrong value, or forgetting to change it in some place you will create potentially hard to find bugs. Especially true if more than one person is working on the project.

It also messes up your source code repository commit history as numbers get changed all over the code rather than in one place.

It also means that when you see some number in the code you have no clue immediately what it is.

See my code snippets above. "SIZE" is defined once and used 7 times. It would be dumb to have to edit 7 places in the code instead of 1 if I want to change it.

A very good principle in writing software is "Do not Repeat Yourself" (DRY).
Memory in C++ is a leaky abstraction .

jojopi
Posts: 3103
Joined: Tue Oct 11, 2011 8:38 pm

### Re: Solution to X-Toolkit and portability.

DavidS wrote:NOTE: I always overload the = operator for the case of both sides being a char *, so I can dirrectly assign strings.
I do not believe that is possible. Do you have a working example?

Paeryn
Posts: 2779
Joined: Wed Nov 23, 2011 1:10 am
Location: Sheffield, England

### Re: Solution to X-Toolkit and portability.

jojopi wrote:
DavidS wrote:NOTE: I always overload the = operator for the case of both sides being a char *, so I can dirrectly assign strings.
I do not believe that is possible. Do you have a working example?
I thought the same thing. Overloaded operators have to take at least one argument that is of a class, you can't change how operators work on basic types. If you could overload char *d = char *s to do a string copy then how would you go about just copying a pointer? e.g.

Code: Select all

``char *string = (char*)malloc(256);``
She who travels light — forgot something.

ejolson
Posts: 4019
Joined: Tue Mar 18, 2014 11:47 am

### Re: Solution to X-Toolkit and portability.

Heater wrote:\$ ./bigarray
function1: 13042701
function2: 226281
function3: 226305
I've verified these timing on my Pi 3. Note that the Linux virtual memory subsystem is still allocating pages the first time through the loops. This incurs an overhead of about 0.5 seconds which seems to be added to the function1 timing in your example. The library function wmemset can also be used to initialize an array of integers. Interestingly the library function is no faster than the loop. This either means that the loop is optimal or the library is not. Is 1688 MB/s the correct memory bandwidth for the Pi 3? Roylongbottom claims the Pi 3 has a double-pumped DDR2 memory bus speed of 900 MHz with a bus width of 4 bytes for a theoretical bandwidth of 7200 MB/s.

Note, in the linear algebra example I gave, that the carefully-optimized library function performs about 25 times faster than the parallel version of the naive C code. However, in the memory initialization case discussed here, the processor is generally stalled waiting for memory. As there are no computations, there appears to be little opportunity for further optimization. For reference, my verification of your results is

Code: Select all

``````      Name         Seconds         Mbyte/s
function1       12.257853           31.12
function2        0.225985         1688.03
wmemset        0.226488         1684.28``````
given by the source code

Code: Select all

``````#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/time.h>
#include <assert.h>
#include <wchar.h>

#define N 10000

int A[N][N];

static double tic_time;
void tic() {
struct timeval tp;
gettimeofday(&tp,0);
tic_time=tp.tv_sec+tp.tv_usec*1.e-6;
}
double toc() {
struct timeval tp;
gettimeofday(&tp,0);
return tp.tv_sec+tp.tv_usec*1.e-6-tic_time;
}

void function1(){
for(int i=0;i<N;i++) for(int j=0;j<N;j++)
A[j][i]=1;
}
void function2(){
for(int i=0;i<N;i++) for(int j=0;j<N;j++)
A[i][j]=1;
}

void check(int c){
for(int i=0;i<N;i++) for(int j=0;j<N;j++) {
if(A[i][j]!=c) {
printf("check %d failed!\n",c);
return;
}
}
}

#define TNUM 3
int main(){
double secs[TNUM];
char *name[TNUM];

memset(A,0,sizeof(int)*N*N);
tic();
function1();
secs[0]=toc();
name[0]="function1";
check(1);

memset(A,0,sizeof(int)*N*N);
tic();
function2();
secs[1]=toc();
name[1]="function2";
check(1);

assert(sizeof(wchar_t)==sizeof(int));
memset(A,0,sizeof(int)*N*N);
tic();
wmemset((wchar_t *)A,1,N*N);
secs[2]=toc();
name[2]="wmemset";
check(1);

printf("%10s %15s %15s\n",
"Name","Seconds","Mbyte/s");
double MB=(double)N*N*sizeof(int)/1024/1024;
for(int i=0;i<TNUM;i++){
printf("%10s %15.6f %15.2f\n",
name[i],secs[i],MB/secs[i]);
}
return 0;
}``````
Last edited by ejolson on Sat Nov 19, 2016 6:25 pm, edited 4 times in total.

Heater
Posts: 14220
Joined: Tue Jul 17, 2012 3:02 pm

### Re: Solution to X-Toolkit and portability.

ejolson,
Note that the Linux virtual memory subsystem is still allocating pages the first time through the loops. This incurs an overhead of about 0.5 seconds which seems to be added to the function1 numbers in your example.
That is a very good point.

In Linux just because you think you have a pile of memory, statically allocated or obtained via malloc/new, does not mean you actually have it. For example malloc() may return success when you ask for 500MB of RAM but your program may well be killed when it tries to access said RAM because it does not actually exist at the time!

To that end, if you are writing some embedded application that requires some sence of reliability and real-time operation it is a good idea to touch at least one byte in every memory block you have obtained prior to starting the real work.

OK. So let's put my test into a loop and remove the allocation overhead as it runs. We get results like so:

\$ g++ -O3 -Wall -o bigarray -std=c++11 bigarray.cpp
\$ ./bigarray
function1: 13034587
function2: 226449
function3: 226194
function1: 12455339
function2: 226383
function3: 226335
function1: 12454859
function2: 226277
function3: 226306
...

Hmm...not bad really. The overhead of allocating all that memory the first run through was only about 10%. The slowest function went from about 13 seconds to about 12. Of course the subsequent functions saw no gains as the memory was already in place by then.

I have no idea what wmemcpy does. But I must say that my examples were not really about simply initializing memory but rather the difference in traversing an array in a cache friendly way or not. For example when image processing or some such.
Memory in C++ is a leaky abstraction .

ejolson
Posts: 4019
Joined: Tue Mar 18, 2014 11:47 am

### Re: Solution to X-Toolkit and portability.

Heater wrote:Hmm...not bad really. The overhead of allocating all that memory the first run through was only about 10%.
Right, but if the other function had been tested first the overhead would have been 200%, which is what happened in the original version of my code. Note also by populating the page table column first as you have, the physical memory is not contiguous. Thus, performance when traversing the matrix in row-major order for the second test may also be lower than it needs to be. In this case, that appears insignificant, however, I've seen architectures where it makes a bigger difference. Maybe the page size was smaller in those cases.

Also the function I actually used was wmemset. I'm sorry to have mistyped this as wmemcpy in the first version of my post. It has been corrected now.

tufty
Posts: 1456
Joined: Sun Sep 11, 2011 2:32 pm

### Re: Solution to X-Toolkit and portability.

Heater wrote:By the way, I don't like to see that union in there:

union { //Icon Data.
char t[12]; //If a string.
long i[3]; //If inderected.
}dta;

That kind of thing is not portable, could have endianness issues.
"Could have", indeed. Things wrong with it that leap out at me:

- Assumption that char is 8 bits. This isn't even implied by the C standard. You want 8 bits exactly, use <u>int8_t or maybe byte.
- char may or may not be signed, this may well cause you "issues" if you start doing math on the values stored within as though they were simple unsigned byte values.
- Assumption that an indexable array of n 8-bit values will be stored in n consecutive bytes, and not packed to consecutive addressable locations (which is, IIRC, allowable)
- Assumption that long is 32 bits. Again, not even implied, only that it will be at least 32 bits Again, if you want exactly 32 bits, use an explicitly sized type like (in this case) <u>int32_t
- Endianness will almost certainly cause portability "issues" if people start messing with the long values directly.

And that's only the ones that I can think of right now.

DavidS
Posts: 4334
Joined: Thu Dec 15, 2011 6:39 am
Location: USA
Contact: Website

### Re: Solution to X-Toolkit and portability.

jojopi wrote:
DavidS wrote:NOTE: I always overload the = operator for the case of both sides being a char *, so I can dirrectly assign strings.
I do not believe that is possible. Do you have a working example?
C++?
RPi = The best ARM based RISC OS computer around
More than 95% of posts made from RISC OS on RPi 1B/1B+ computers. Most of the rest from RISC OS on RPi 2B/3B/3B+ computers

DavidS
Posts: 4334
Joined: Thu Dec 15, 2011 6:39 am
Location: USA
Contact: Website

### Re: Solution to X-Toolkit and portability.

tufty wrote:
Heater wrote:By the way, I don't like to see that union in there:

union { //Icon Data.
char t[12]; //If a string.
long i[3]; //If inderected.
}dta;

That kind of thing is not portable, could have endianness issues.
"Could have", indeed. Things wrong with it that leap out at me:

- Assumption that char is 8 bits. This isn't even implied by the C standard. You want 8 bits exactly, use <u>int8_t or maybe byte.
- char may or may not be signed, this may well cause you "issues" if you start doing math on the values stored within as though they were simple unsigned byte values.
- Assumption that an indexable array of n 8-bit values will be stored in n consecutive bytes, and not packed to consecutive addressable locations (which is, IIRC, allowable)
- Assumption that long is 32 bits. Again, not even implied, only that it will be at least 32 bits Again, if you want exactly 32 bits, use an explicitly sized type like (in this case) <u>int32_t
- Endianness will almost certainly cause portability "issues" if people start messing with the long values directly.

And that's only the ones that I can think of right now.
Those are only problems if you are using a system where the assumptions are not true. With every compiler on RISC OS so far those assumptions are always true, and the target is RISC OS, nothing else.
RPi = The best ARM based RISC OS computer around
More than 95% of posts made from RISC OS on RPi 1B/1B+ computers. Most of the rest from RISC OS on RPi 2B/3B/3B+ computers

DavidS
Posts: 4334
Joined: Thu Dec 15, 2011 6:39 am
Location: USA
Contact: Website

### Re: Solution to X-Toolkit and portability.

Heater wrote:I thnk 99 prcnt of prgrmmrs wld sy tht rmving vwls frm wrds to mk vrble nms is unncssry nd mks lgblty hrdr.

I liked x0, y0, x1, y1.
So if you want readability DO NOT use defined constants

If you have some constant that is reused many times in your program, perhaps in many files, then when you want to change it you will have to find it all those files and change it in many places.

It also means that if you make a mistake, a typo or just selecting the wrong value, or forgetting to change it in some place you will create potentially hard to find bugs. Especially true if more than one person is working on the project.

It also messes up your source code repository commit history as numbers get changed all over the code rather than in one place.

It also means that when you see some number in the code you have no clue immediately what it is.

See my code snippets above. "SIZE" is defined once and used 7 times. It would be dumb to have to edit 7 places in the code instead of 1 if I want to change it.

A very good principle in writing software is "Do not Repeat Yourself" (DRY).
Ok I see your point for removing vowels, that I do not always do.

There is nothing wrong with using defined constants, so long as its value is clear WHERE IT IS USED and the definition is in the same file as the usage, yes this may mean some copy and paste (or save selection to open file on RISC OS) when multiple files require the value, though that is only a couple of keystrokes per file (or one quick mouse movement on RISC OS).
RPi = The best ARM based RISC OS computer around
More than 95% of posts made from RISC OS on RPi 1B/1B+ computers. Most of the rest from RISC OS on RPi 2B/3B/3B+ computers

Heater
Posts: 14220
Joined: Tue Jul 17, 2012 3:02 pm

### Re: Solution to X-Toolkit and portability.

ejolson,
...if the other function had been tested first the overhead would have been 200%,..
Also a good point.

OK, let's strip the thing down to the one function that is a) Fastest and b) the least obfuscated way to write it:

I get a result like so:

\$ g++ -O3 -Wall -o bigarray -std=c++11 bigarray.cpp
\$ ./bigarray
function: 592413
function: 227601
function: 226396
function: 226435
function: 227746
function: 226373

Sure enough, as you say, the first run through is 3 times slower as all the memory gets acquired.

Now, if you want more speed what can you do? Unrolling loops or even removing them makes negligible difference. One could resort to assembler or some funky platform specific vector thing. All at the cost of portability. Is it worth it?
Memory in C++ is a leaky abstraction .

Heater
Posts: 14220
Joined: Tue Jul 17, 2012 3:02 pm

### Re: Solution to X-Toolkit and portability.

DavidS,
There is nothing wrong with using defined constants, so long as its value is clear WHERE IT IS USED...
Are your deliberately winding us up here?

To take my previous example, if the value were clear where it is used then instead of:

#define SIZE 10000

I would have to have:

#define SIZE_10000 10000

Or perhaps when dealing with bit fields I would have:

#define IRQ_ENABLE_03 0x03

#define IRQ_ENABLE 0x03

Which of course defeats the whole point of defined constants in the first place.
...and the definition is in the same file as the usage,
Which again defeats the whole point of defining things in a single place. Remember "DRY".
...yes this may mean some copy and paste...
Yes, and I have seen so many bugs caused by copy and paste errors.

Aside: I once worked on a team with a guy whose surname was Woodcock. He made so many mistakes by copying and pasting code around and failing to adapt it correctly to it's new home that we started to talk about "Woodcock and Paste errors"
Memory in C++ is a leaky abstraction .

ejolson
Posts: 4019
Joined: Tue Mar 18, 2014 11:47 am

### Re: Solution to X-Toolkit and portability.

Heater wrote:One could resort to assembler or some funky platform specific vector thing. All at the cost of portability. Is it worth it?
If you look at the Linux kernel source code, there are multiple assembler subroutines for initializing and copying memory. Apparently these operations are important and affect total system performance enough to warrant tuned machine-level optimization.

In the current case, the 1866 MB/s speed obtained by a simple loop in C might already be optimal. Since the C standard library on Raspbian was compiled to be armv6 compatible with the original Pi B, it is not so surprising that wmemset performs the same as the simple loop. If the loop is not optimal on the Pi 3, it would be interesting to know how close to the theoretical bandwidth of 7200 MB/s is possible. The needed code has likely already been written and can be found in the Linux kernel.

Heater
Posts: 14220
Joined: Tue Jul 17, 2012 3:02 pm

### Re: Solution to X-Toolkit and portability.

ejolson,
If you look at the Linux kernel source code, there are multiple assembler subroutines for initializing and copying memory. Apparently these operations are important and affect total system performance enough to warrant tuned machine-level optimization.
Yes indeed.

I think the keywords here are "total system performance". That is to say that these optimizations affects millions of people and computers on many different architectures all over the world. It's good someone has the time to optimize all of that.

How often is that true of your day to day applications? That you need to be portable?

My feeling is that it's not much.

I'm not sure where you are going with the memory bandwidth thing. As I said my examples were not about simply initializing a big array. When you start to consider what apps do: network/file access, thread context switching, etc, etc, this can get lost in the noise of performance hindrances.
Memory in C++ is a leaky abstraction .

DavidS
Posts: 4334
Joined: Thu Dec 15, 2011 6:39 am
Location: USA
Contact: Website

### Re: Solution to X-Toolkit and portability.

Heater wrote:DavidS,
There is nothing wrong with using defined constants, so long as its value is clear WHERE IT IS USED...
Are your deliberately winding us up here?

To take my previous example, if the value were clear where it is used then instead of:

#define SIZE 10000

I would have to have:

#define SIZE_10000 10000

Or perhaps when dealing with bit fields I would have:

#define IRQ_ENABLE_03 0x03

#define IRQ_ENABLE 0x03

Which of course defeats the whole point of defined constants in the first place.
No your use of SIZE was very clear, and exactly what I mean as the way to do defined constants, someone reading the code can quickly see the current value of SIZE, that is the important thing. You do not need to include the value at the location of use, only make it clear at the point of usage.

Why should I have to look at a header file to figure out what the currently set values of the constants are. Or worse in many cases find that I have to look in a header file that is included by a header file, that in turn is included by the header file in the code, and in some cases have multiple related constants defined in multiple different header files that are included through other header files, all so that I know what the value is so that I can do something with the code.

Ok it is not so bad in the case where there is only one header file to deal with, though that is a rarity.

This issue is also directly related to why it is a good idea to keep each source file as short as reasonable. It gives you the ability to see everything going on in a given source file. Also related to why each functions/procedure/subroutine should be kept short.

Though I am guessing that you do not believe in the short source file, based on your like of digging through headers?
...and the definition is in the same file as the usage,
Which again defeats the whole point of defining things in a single place. Remember "DRY".
...yes this may mean some copy and paste...
Yes, and I have seen so many bugs caused by copy and paste errors.
As have I, though never form copied constant values that are used in multiple source files.

Aside: I once worked on a team with a guy whose surname was Woodcock. He made so many mistakes by copying and pasting code around and failing to adapt it correctly to it's new home that we started to talk about "Woodcock and Paste errors"
I will have to remember that one .
RPi = The best ARM based RISC OS computer around
More than 95% of posts made from RISC OS on RPi 1B/1B+ computers. Most of the rest from RISC OS on RPi 2B/3B/3B+ computers

ejolson
Posts: 4019
Joined: Tue Mar 18, 2014 11:47 am

### Re: Solution to X-Toolkit and portability.

Heater wrote:How often is that true of your day to day applications? That you need to be portable?
The need for high-speed portable applications is exactly the reason behind the API's in LAPACK, BLAS, OpenCV, FFTW and even the C standard library. Unfortunately for Pi 3 users, the system libraries in Raspbian are slow in order to be reverse compatible with the Pi B.

On the other hand, writing your own loops for a linear algebra solver or a memory initialization routine has merit from an educational point of view, especially if coupled with a discussion of computer architecture and code optimization. After all, the whole idea of Raspberry Pi is to create a computer literate society.

Paeryn
Posts: 2779
Joined: Wed Nov 23, 2011 1:10 am
Location: Sheffield, England

### Re: Solution to X-Toolkit and portability.

DavidS wrote:
jojopi wrote:
DavidS wrote:NOTE: I always overload the = operator for the case of both sides being a char *, so I can dirrectly assign strings.
I do not believe that is possible. Do you have a working example?
C++?
The assignment operator can't be overloaded at the global level, it's got to exist as a member function of some class, it's purpose is to allow assigning to an object of said class. You can't create a class of type char, nor alter the built in type so how are you managing to overload the assignment operator to allow copying a string from one char * to another string of char *? Especially as when the left hand side of an assignment operator is a pointer to something (as in this case) then a value will be stored there as if it were an address, no calls to any operator=() function are made to allow you to do any sort of deep copying.
She who travels light — forgot something.

DavidS
Posts: 4334
Joined: Thu Dec 15, 2011 6:39 am
Location: USA
Contact: Website

### Re: Solution to X-Toolkit and portability.

Paeryn wrote:
DavidS wrote:
jojopi wrote:I do not believe that is possible. Do you have a working example?
C++?
The assignment operator can't be overloaded at the global level, it's got to exist as a member function of some class, it's purpose is to allow assigning to an object of said class. You can't create a class of type char, nor alter the built in type so how are you managing to overload the assignment operator to allow copying a string from one char * to another string of char *? Especially as when the left hand side of an assignment operator is a pointer to something (as in this case) then a value will be stored there as if it were an address, no calls to any operator=() function are made to allow you to do any sort of deep copying.
If those are the limits imposed by GCC that would explain why some of my code does not work.

To bad that C++ was not standardized before many were already using it, and the differences in implementation had already spread to the point that the standard is nearly meaningless.
RPi = The best ARM based RISC OS computer around
More than 95% of posts made from RISC OS on RPi 1B/1B+ computers. Most of the rest from RISC OS on RPi 2B/3B/3B+ computers