Trying to decide which programming language I want to learn

  • Thread starter yungman
  • Start date
  • #376
33,967
5,624
In the real world, it sometimes does make a big difference when you use one line of code instead of two, especially in loops that run a large number of times.
For that to be significant in a particular situation you would have to know that
  1. the harder-to-maintain code does actually provide a performance gain; and
  2. the cost of harder-to-maintain code is outweighed in this particular situation by the value gained by performance improvement
I'm very much not a fan of difficult-to-maintain code, but in certain rare instances its use is justified. Any such uses should be well-documented via code comments.

With regard to item 2 above, I've written assembly code (using AVX-512 instructions) that outperforms optimized C code. The code, both C and assembly, iterates through fairly large arrays of floats and picks out all of the array elements that are larger than some specified value. I've timed both code chunks, and my assembly code runs faster than the C code, even when the C code is fully optimized for speed.

Compilers usually produce well-optimized code, but if you know what you're doing, you can sometimes write code that is faster.
 
  • Like
Likes sysprog
  • #377
1,632
884
Sorry if I offended you.
Thanks for that, but please Sir, I didn't mean to suggest that I had taken offense; you said,
yungman said:
My idea of a good program is clean, easy to understand, straight forward rather than saving one line. Makes no difference in real world whether the program is 20 lines or 21 lines.
and I replied to that with:
sysprog said:
That attitude won't help you to win a code golf greenbar jacket.
That was on my part an attempt at being wry ##-##'code golf' is a programmers' game in which coders compete to accomplish something using the fewest lines or fewest characters, analogous to golfers winning by getting the ball in the hole using the fewest strokes; the 'green jacket' is a golf award inclusive of an actually wearable green jacket; my reference to a "greenbar jacket" for code golf winners was a reference to greenbar line printer paper that uses two light shades of green, one rather darker than the other but both much lighter (hopefully) than the black printer ink, to make a printout easier to pick out groups of a few print lines in ##-## if there isn't really such a thing as a similarly shaded greenbar jacket, well then I propose that there should be for code golf winners ##-##
 
Last edited:
  • #378
1,632
884
I'm very much not a fan of difficult-to-maintain code, but in certain rare instances its use is justified. Any such uses should be well-documented via code comments.

With regard to item 2 above, I've written assembly code (using AVX-512 instructions) that outperforms optimized C code. The code, both C and assembly, iterates through fairly large arrays of floats and picks out all of the array elements that are larger than some specified value. I've timed both code chunks, and my assembly code runs faster than the C code, even when the C code is fully optimized for speed.

Compilers usually produce well-optimized code, but if you know what you're doing, you can sometimes write code that is faster.
I think that @Mark44's PF Insights articles on the use of the Intel AVX-512 architecture are the best guides on that topic that I could find.
 
  • Like
Likes Mark44
  • #379
pbuk
Science Advisor
Gold Member
1,621
538
I'm very much not a fan of difficult-to-maintain code, but in certain rare instances its use is justified. Any such uses should be well-documented via code comments.

With regard to item 2 above, I've written assembly code (using AVX-512 instructions) that outperforms optimized C code. The code, both C and assembly, iterates through fairly large arrays of floats and picks out all of the array elements that are larger than some specified value. I've timed both code chunks, and my assembly code runs faster than the C code, even when the C code is fully optimized for speed.

Compilers usually produce well-optimized code, but if you know what you're doing, you can sometimes write code that is faster.
Completely agree - if you really need to speed up a tight loop then providing you are willing to restrict your target processor then assembly is the way to go. With a device driver this is always the case, although with say a numerical analysis routine you might want portability so some horrible, hacky (but well commented) c code is indeed best.

However none of this applies for routine use of a[i++]. In fact I have done some experimenting and you might be surprised to find that this quick-to-type shortcut can actually produce SLOWER code!

Edit: as well as being off-topic what follows is original research but I hope it will be allowed to remain as a point of interest. Probably best not to follow up here though!

Source code (simple Fibonnaci sequence):
#include <iostream>
using namespace std;
int main() {
  int a[100] = {0, 1};
  int k = 2;
  int f_k_minus_1 = 1;
  int f_k_minus_2 = 1;
  int f_k;
  while (f_k < 1000) {
    f_k = f_k_minus_1 + f_k_minus_2;
    f_k_minus_2 = f_k_minus_1;
    f_k_minus_1 = f_k;
    // Either (bad):
    a[k++] = f_k;
    // Or (good):
    // a[k] = f_k;
    // k++;
  }
  cout << a[k - 1];
  return 0;
}
Compiled using g++ -S (i.e. no optimisation) g++ version 7.5.0 on x86_64
The generated code saves all intermediate results to memory. The 'bad' code uses an additional register for the incremented value of k which it then saves to memory.
Using a[k++]:
        movl    -432(%rbp), %eax        ; Load k into ax.
        leal    1(%rax), %edx           ; Load k + 1 into dx.
        movl    %edx, -432(%rbp)        ; Save dx into k.
The 'good' code simply increments k in place. This saves an instruction but adds an additional fetch from (cached) memory to reload k into the ALU.
Using a[k]; k++:
        movl    -432(%rbp), %eax        ; Load k into ax.
        ; do some other stuff.
        addl    $1, -432(%rbp)          ; Add 1 to k;
Compiled using g++ -S -O g++ version 7.5.0 on x86_64
Now the compiler is not worried about an informative core dump for debugging it uses registers for all intermediate values and comes up with almost the same object code for both 'good' and 'bad' sources - but separating the increment from the array indexing still saves 1 instruction, although only on the first iteration of the loop. This time I am going to show the whole loop for each case.
Using a[k++]:
.L3:
        leal    (%rsi,%rcx), %edx
        movl    %eax, %edi
        movl    %edx, -4(%rsp,%rax,4)
        addq    $1, %rax
        movl    %ecx, %esi
        movl    %edx, %ecx
        cmpl    $999, %edx
        jle     .L3
Using a[k]; k++:
        jmp     .L3
.L6:
        movl    %edx, %ecx
.L3:
        leal    (%rcx,%rsi), %edx
        movl    %edx, -4(%rsp,%rax,4)
        movl    %eax, %edi
        addq    $1, %rax
        movl    %ecx, %esi
        cmpl    $999, %edx
        jle     .L6

Conclusion: a[i++]; may be quicker to type but is harder to maintain and may well run slower than a[i]; i++;.
 
Last edited:
  • Like
Likes sysprog
  • #380
503
195
I wouldn't be surprised if a c compiler accepted, and processed properly, something to the effect of a[0++], but it also wouldn't surprise me if it didn't : since the 0 is procedure-embedded, that's explicitly "self modifying code", might give a run-time OS hissy-fit. And, it might preclude an optimizer from reserving and using a register, which would be faster.

You could always split the difference :
register int i=0 ; ...a[i++] ;
 
  • #381
4,754
102
Thanks for that, but please Sir, I didn't mean to suggest that I had taken offense; you said,
and I replied to that with:
That was on my part an attempt at being wry ##-##'code golf' is a programmers' game in which coders compete to accomplish something using the fewest lines or fewest characters, analogous to golfers winning by getting the ball in the hole using the fewest strokes; the 'green jacket' is a golf award inclusive of an actually wearable green jacket; my reference to a "greenbar jacket" for code golf winners was a reference to greenbar line printer paper that uses two light shades of green, one rather darker than the other but both much lighter (hopefully) than the black printer ink, to make a printout easier to pick out groups of a few print lines in ##-## if there isn't really such a thing as a similarly shaded greenbar jacket, well then I propose that there should be for code golf winners ##-##
Ha ha, you got me. I don't know anything about golf. the closest thing I was to golf was my grandson want to see how's the inside of golf balls looked like, so I bought one and cut it open!!!

I know there are people particular in class that jump through hoops to get rid of one or two lines in coding, going through all the network theorems in electronics to save a few components. That's good in the class, but not necessary in the real world. I actually had a debate on the EE forum here a week or so ago that I am an advocate that I work hard to make all my design as simple as possible so people see understand and predict all the current and voltages just with simple V=IR. That if you have to use network theorem to find the voltages and current, that's too complicated. Of cause, one cannot avoid that completely, but at least make an afford to keep it straight forward. For both maintenance and documentation. Be ingenious in the whole system, not the every little circuit(in this case, the code lines).

I have no issue using i++ or ++i type of post and pre increments. Where I draw the line is array[i++]. You have to look around to find the value of "i" to know the number already. Then you have to think about it's not the array you are looking, you have to look at array[i+1]. Never mind it's harder for others to look at. hell, at my old age, I likely get tripped if I look at the program 2 weeks later!!! That's old age for you!!!
 
  • Like
Likes pbuk
  • #382
33,967
5,624
wouldn't be surprised if a c compiler accepted, and processed properly, something to the effect of a[0++]
I would be very surprised to find any C compiler that accepted this. A constant is not an l-value, so can't be assigned to or incremented or otherwise treated like a variable.
 
  • Like
Likes pbuk and sysprog
  • #383
33,967
5,624
Be ingenious in the whole system, not the every little circuit(in this case, the code lines).
As far as program optimization is concerned, that's pretty much the opposite of good advice. For optimization, there's the 80-20 rule: 20% of the program takes 80% of the time.

Of course you want to write good code, but you never want to optimize the entire program -- just the part that's most active. And that's exactly what profilers are good for.
Where I draw the line is array[i++]. You have to look around to find the value of "i" to know the number already. Then you have to think about it's not the array you are looking, you have to look at array[i+1].
No. You have this all wrong.
To evaluate array[i++], you just need to know array[ i]. The index i gets incremented after it is used as the index.

For example, if i == 1, and the array is initialized with {2, 4, 6}, then array[i++] == 4. Sometime after that, i gets incremented to 2.

On the other hand, if i == 1, then i is incremented to 2, so array[++i] == 6.

This ain't rocket science.
 
  • Like
Likes sysprog
  • #384
4,754
102
I have questions with this reading numbers from file and write into vector and display the numbers.
C++:
// Read content of file to array
#include <iostream>
#include <fstream>
#include <vector>
using namespace std;

//void getNum(int arr[], int size);
ifstream inFile;
int main()
{

    int count = 0;
    int num;
    int x = 0;
    vector<int>dynArr(3);
    inFile.open("C:\\Users\\alanr\\Desktop\\C++ exercise\\Gaddis\\inout files\\array1.txt");
    if (inFile.fail())
    {
        cout << " Fail to open file\n\n";
    }
    else
    {
        while (inFile >> num)
        {
            //cout << num << " ";
            dynArr.push_back(num);// Read from file and write to vector num.
        }
        cout << " going to read dynArr\n\n";
        while (!dynArr.empty())
        {
            num = dynArr[x];
            cout << num << " ";// display all the numbers until the end.
            x++;
        }
    }
    cout << "\n\n";
    return 0;
}
It kind of doing what I want, BUT there's a warning as shown when I ran the program that the vector is out of bound.
Compile error file write.jpg


It's only defined as 3 element vector, small and I only read in 5 numbers. It cannot be out of bound!!


Also if you look at line 32 of the code and look at the top left corner of picture above, I expect to display 12345. But I got 00012345. AND the 12345 is at the right side of the stream. Is it when I use dynArr.push_back, I pushed the 5 numbers AFTER the 3 empty elements? That's the reason I got 000 in front?

Bottom line, I am not sure whether I did it right. I am pushing my knowledge on this one.
I read from file that has 5 numbers( I already verify I can do it with 3 numbers, 6 numbers).
1) I use ( inFile.fail()) to check for EOF. Then read the numbers one by one into a variable num, then write into the vector dynArr using dynArr.push_back(num);

2) I use (!dynArr.empty()) to read until I reach the end of the dynArr.

I want to know whether I am doing all these correctly. I am putting a lot of effort in data management, from files to vectors etc. I just feel this is very important in programing instead of just condition statements or loop statements. I want to use dynArr.pop_back next to control the size of the vector. To me, these data management is a lot more difficult.

Thanks
 
Last edited:
  • #385
pbuk
Science Advisor
Gold Member
1,621
538
Comment the first: it's the programmer's job to write understandable code; it's the compiler's job to turn that into efficient code. Where to put increment instructions is an example of something a compiler is very good at, and it's a waste of energy to try and out-optimize the compiler by clever positioning of an increment instruction.
Completely agree.

Comment the second: "understandable code" does not mean "avoid idiomatic constructs of the language". Like it or not, j++ is an idiom of C. Using j=j+1 is taste at best; don't kid yourself to think it's better. That would be like speaking English without contractions.
Agree with reservations - I would say that understandable code includes "be careful that you do not use idiomatic constructs in a way that hides their potential side effects"

Comment the third: a[j++] specifies exactly when j is to be incremented.
Completely disagree. Look at the following:
C:
// The timing of the increment is unspecified. 
b = a[j] + a[j++] 
// In a c++17 implementation the value of a[0] is copied into a[2], in earlier standards
// it is either copied into a[1] or the operation may be unspecified.  
j = 0;
a[j + 1] = a[j++]
https://en.cppreference.com/w/cpp/language/eval_order

Indeed, for reasons that at the time made a lot of sense, I once wrote something like b = function_a(i) + function_b(++i).
You are lucky that worked; recompile it and there is no guarantee it still will. https://en.cppreference.com/w/cpp/language/eval_order
Order of evaluation of any part of any expression, including order of evaluation of function arguments is unspecified (with some exceptions listed below). The compiler can evaluate operands and other subexpressions in any order, and may choose another order when the same expression is evaluated again.

There is no concept of left-to-right or right-to-left evaluation in C++.
A way to write this that is guaranteed to work is:
C:
b = function_a(i) + function_b(i + 1);
i++; // Some people prefer ++i here; the compiler shouldn't care.
Or alternatively:
C:
c = function_a(i);
i++; // Some people prefer ++i here; the compiler shouldn't care.
b = c + function_b(i);
 
  • #386
pbuk
Science Advisor
Gold Member
1,621
538
Sometime after that, i gets incremented to 2.
Indeed. 'sometime after that' is exactly the problem: rather than double-guess the compiler as to when that 'sometime' is, much better to be sure by moving the increment into a separate statement.
 
  • #387
33,967
5,624
Indeed. 'sometime after that' is exactly the problem: rather than double-guess the compiler as to when that 'sometime' is, much better to be sure by moving the increment into a separate statement.
I wrote "sometime after that" because I didn't want to bring up the concept of sequence points. Post-increments and post-decrements occur after sequence points, which are described in this wiki article: https://en.wikipedia.org/wiki/Sequence_point.

In my reply to @yungman's comment about not using increment operators, the example I had in mind was a simple one, with obvious sequence points, like this.
C++:
int array[] = {2, 4, 6};
int i = 1;
int val = array[i++];
val will be set to 4, and "sometime after that"; i.e., after the array expression is evaluated, i will be incremented to 2.
 
  • Like
Likes sysprog
  • #388
1,632
884
I wouldn't be surprised if a c compiler accepted, and processed properly, something to the effect of a[0++], but it also wouldn't surprise me if it didn't : since the 0 is procedure-embedded, that's explicitly "self modifying code", might give a run-time OS hissy-fit. And, it might preclude an optimizer from reserving and using a register, which would be faster.

You could always split the difference :
register int i=0 ; ...a[i++] ;
The compiler would reject the 0++ incrementation directive inside the brackets of the a[0++] expression because 0 is not a variable.
 
  • #389
33,967
5,624
It kind of doing what I want, BUT there's a warning as shown when I ran the program that the vector is out of bound.
That's not a warning -- that's a run-time exception, an error.
It's only defined as 3 element vector, small and I only read in 5 numbers. It cannot be out of bound!!
The vector started out with three elements, all of them 0. Each call to push_back() adds a new number after the three 0 elements.

Your logic to exit the 2nd while loop is faulty. The expression dynArr.empty() is true only if there are no elements in the vector. Since you aren't removing them from the vector, the loop condition won't ever be true.
yungman said:
Also if you look at line 32 of the code and look at the top left corner of picture above, I expect to display 12345. But I got 00012345. AND the 12345 is at the right side of the stream. Is it when I use dynArr.push_back, I pushed the 5 numbers AFTER the 3 empty elements? That's the reason I got 000 in front?
Yes.

I made two changes to your code.
C++:
vector<int>dynArr;
This change eliminates the first three 0 elements.
C++:
unsigned i = 0;
while (i < dynArr.size())
{          
      num = dynArr[i++];
      cout << num << " ";// display all the numbers until the end.          
}
This change eliminates the exception that you saw.
 
Last edited:
  • Like
Likes yungman
  • #390
pbuk
Science Advisor
Gold Member
1,621
538
I wrote "sometime after that" because I didn't want to bring up the concept of sequence points. Post-increments and post-decrements occur after sequence points, which are described in this wiki article: https://en.wikipedia.org/wiki/Sequence_point.
Exactly that. I don't think that it is a good idea to tell someone to do something with subtle side effects unless you are going to explain those side effects.

In my reply to @yungman's comment about not using increment operators, the example I had in mind was a simple one, with obvious sequence points, like this.
C++:
int array[] = {2, 4, 6};
int i = 1;
int val = array[i++];
val will be set to 4, and "sometime after that"; i.e., after the array expression is evaluated, i will be incremented to 2.
That's the problem with bugs during the code lifecycle, they tend to happen when something that looked simple is modified and an unintended side effect crops up - or even worse the side effect goes unnoticed because it doesn't affect anything until 2 years later when you add another feature.

The way to write that code avoiding all of these potential problems without incurring any performance penalty (even if that mattered) is:
C++:
int array[] = {2, 4, 6};
int i = 1;
int val = array[i];
i++;
 
  • #391
503
195
The compiler would reject the 0++ incrementation directive inside the brackets of the a[0++] expression because 0 is not a variable.
I'll take your word for it but, as long as the context is only iteration and not re-iteration (ie: it doesn't need to be rezeroed), it saves a superflous explicit declaration.
 
  • #392
pbuk
Science Advisor
Gold Member
1,621
538
I'll take your word for it but, as long as the context is only iteration and not re-iteration (ie: it doesn't need to be rezeroed), it saves a superflous explicit declaration.
What's wrong with a[1]?
 
  • #393
jtbell
Mentor
15,614
3,638
C++:
unsigned i = 0;
while (i < dynArr.size())
{         
      num = dynArr[i++];
      cout << num << " ";// display all the numbers until the end.         
}
This is of course also a common application for a traditional for-loop:
C++:
for (int i = 0; i < dynArr.size(); i++)
{
    cout << dynArr[i] << " ";
}
Or in a modern (post 2011) version of C++, a range-based for-loop:
C++:
for (int num : dynArr)
{
    cout << num << " ";
}
 
  • Like
Likes pbuk
  • #394
503
195
What's wrong with a[1]?
Nothing, if your purpose is to explicitly access the second element in an array. In what context were you comparing it to a hypothetical a[0++] statement ?
 
  • #395
33,967
5,624
What's wrong with a[1]?
Nothing, if your purpose is to explicitly access the second element in an array. In what context were you comparing it to a hypothetical a[0++] statement ?
I believe pbuk was referring to this post of yours, #380:
wouldn't be surprised if a c compiler accepted, and processed properly, something to the effect of a[0++]
There's nothing hypothetical about a[0++]. It just flat won't compile.
 
  • #396
33,967
5,624
This thread is getting very long, with just under 400 posts. @yungman, when you have another question, please start a new thread.
 
  • Like
Likes pbuk
  • #397
4,754
102
Thank guys, my big boss just had a hip replacement yesterday and she's home. I am too busy right now to read the replies right now. I'll get to it later tonight or tomorrow.

thanks
 
  • #398
33,967
5,624
I'm closing this thread now. If anyone other than the OP (@yungman) feels the need to reply to a post here, please let me know, and I'll reopen it temporarily.
 

Related Threads on Trying to decide which programming language I want to learn

Replies
7
Views
4K
  • Last Post
Replies
14
Views
3K
  • Last Post
2
Replies
43
Views
3K
  • Last Post
Replies
9
Views
890
Replies
17
Views
2K
  • Last Post
2
Replies
29
Views
2K
Replies
9
Views
856
Replies
15
Views
3K
Top