Vector access speed, which method is faster? - c++

I'm curious which method is faster when accessing vectors.
For the sake of simplicity, lets say I have two objects: Player and Ship.
There's a vector of player pointers vector<Player*> players and each player object contains a vector of ship pointers vector<Ship*> ships, and then each ship has several functions that it can call, and so on.
In these situations, is it faster to access these functions directly? Or to create a temporary object pointer to access everything?
Is it faster to do this:
for (int i = 0; i < players.size(); i++)
{
for (int j = 0; j < players.at(i)->ships.size(); j++)
{
players.at(i)->ships.at(j)->update();
if (
(players.at(i)->ships.at(j)->get_x() > 0) &&
(players.at(i)->ships.at(j)->get_x() < screen_x) &&
(players.at(i)->ships.at(j)->get_y() > 0) &&
(players.at(i)->ships.at(j)->get_y() < screen_y)
)
{
players.at(i)->visible.push_back(j);
}
}
}
Or is it faster to create temporary pointers so that the vectors don't need to be continually accessed:
for (int i = 0; i < players.size(); i++)
{
Player* play = players.at(i);
for (int j = 0; j < play->ships.size(); j++)
{
Ship* ship = play->ships.at(j);
ship->update();
int ship_x = ship->get_x();
int ship_y = ship->get_y();
if (
(ship_x > 0) &&
(ship_x < screen_x) &&
(ship_y > 0) &&
(ship_y < screen_y)
)
{
play->visible.push_back(j);
}
}
}
I know the second is visually neater, but don't really know if it's necessarily faster.
Thoughts?

Seems to me that the emphasis on speed is misplaced. I think you should start by writing the code to be more readable:
auto is_visible = [=](Ship const &s) { return s.get_x() > 0 && s.get_x() < screen_x
&& s.get_y() > 0 && s.get_y() < screen_y;
};
for (auto & player : players)
std::copy_if(ships.begin(), ships.end(),
std::back_inserter(player.visible),
is_visible);
At least IMO, this is at least as safe as using at for indexing, but probably at least as fast as using [], and more readable than either one.
I should probably add one more point: visibility doesn't seem to depend on the player. At least from the way the code's written, all the players will have the same set of visible ships. If that's correct, you probably want to do something more like:
std::vector<Ship> visible;
std::copy_if(ships.begin(), ships.end(),
std::back_inserter(visible),
[=](Ship const &s) { return s.get_x() > 0 && s.get_x() < screen_x
&& s.get_y() > 0 && s.get_y() < screen_y; });
for (auto &player : players)
player.visible = visible;

You should check and see which one is faster.
It might be the first or it might be the second. It will definitely be the first if most ships' X coordinate is negative.
However, if the second one looks better to you (it does to me, too), stick with it. Worry about performance when there's an actual performance issue.

I think you're at the mercy of your optimizing compiler here. Either one might be faster, depending on how it gets optimized.
In the first version, it's possible that the compiler will decide to
pull out the players.at(i)->ships.at(j) common subexpression,
possibly with the get_x() or get_y() turning it into something
that looks a lot like your second version.
In the second version, it's possible that reordering could move the
int ship_y = ship->get_y() into the loop conditional so that it can
short circuit with ship_y > 0.
In both, it might decide to turn the entire short circuit conditional
into a sequence of fast bitwise and instructions, eliminating
branches
But my guess is that you're not going to see much difference either way. Try dumping the assembly code to compare, and of course, profile it.

Thanks for the info everyone. Since there wasn't a clear cut "option A is definitely faster than option B", I took your advice and did a bench test.
Here's some code I put together.
Basically, it creates 100 players. Each player has a vector of 100 ships. Each ship has a vector of 100 crew. (Once it ran, it consumed about 500MB of RAM).
I ran the test both unoptimized and optimized (the -O3 flag)
Test 1 was the chain pointers (ie. player->ship->crew->number, etc.)
Test 2 was the same as Test 1, but I replaced all the .at() with operator[].
Test 3 was using temporary pointers to access everything.
I ran each test multiple times and averaged the results.
Here's my results:
Unoptimized:
Test 1: 13000
Test 2: 5500
Test 3: 2800
Optimized:
Test 1: 1050
Test 2: 650
Test 3: 450
It shows that optimizing greatly increased speed in all cases.
Either way though, optimized or unoptimized, .at() definitely slows things WAY down. Using the operator[] was significantly faster.
But in the end, using a temporary pointer was the fastest in all cases.
#include <vector>
#include <ctime>
#include <iostream>
using namespace std;
class People
{
public:
vector<int> number;
};
class Ship
{
public:
Ship(int f);
vector<People*> crew;
int get_x();
int get_y();
private:
int x;
int y;
};
Ship::Ship(int f)
{
//Assign some nonsense for testing purposes
x = f * 50;
y = f * 75;
}
int Ship::get_x()
{
return x;
}
int Ship::get_y()
{
return y;
}
class Player
{
public:
vector<Ship*> ships;
};
int main(int argc, char *argv[])
{
vector<Player*> players;
int start, end;
unsigned int i, j, k, l;
//Create 100 players, each with 100 ships, and each ship with 100 crew.
for (i = 0; i < 100; i++)
{
Player* play = new Player;
players.push_back(play);
for (j = 0; j < 100; j++)
{
Ship* new_ship = new Ship(j);
play->ships.push_back(new_ship);
for (k = 0; k < 100; k++)
{
People* newbie = new People;
new_ship->crew.push_back(newbie);
for (l = 0; l < 100; l++)
{
newbie->number.push_back(0);
}
newbie->number.clear();
}
}
}
//Test 1
start = clock();
for (i = 0; i < players.size(); i++)
{
for (j = 0; j < players.at(i)->ships.size(); j++)
{
for (k = 0; k < players.at(i)->ships.at(j)->crew.size(); k++)
{
for (l = 0; l < 100; l++)
{
//Give each crew some number to hold on to.
players.at(i)->ships.at(j)->crew.at(k)->number.push_back(players.at(i)->ships.at(j)->get_x() * players.at(i)->ships.at(j)->get_y() + l);
}
//Clear the number list for the next test.
players.at(i)->ships.at(j)->crew.at(k)->number.clear();
}
}
}
end = clock();
cout << "Test 1: " << (end - start) << endl;
//Test 2
start = clock();
for (i = 0; i < players.size(); i++)
{
for (j = 0; j < players[i]->ships.size(); j++)
{
for (k = 0; k < players[i]->ships[j]->crew.size(); k++)
{
for (l = 0; l < 100; l++)
{
players[i]->ships[j]->crew[k]->number.push_back(players[i]->ships[j]->get_x() * players[i]->ships[j]->get_y() + l);
}
players[i]->ships[j]->crew[k]->number.clear();
}
}
}
end = clock();
cout << "Test 2: " << (end - start) << endl;
//Test 3
start = clock();
for (i = 0; i < players.size(); i++)
{
Player* temp_play = players.at(i);
for (j = 0; j < temp_play->ships.size(); j++)
{
Ship* temp_ship = temp_play->ships.at(j);
for (k = 0; k < temp_ship->crew.size(); k++)
{
People* temp_crew = temp_ship->crew.at(k);
for (l = 0; l < 100; l++)
{
temp_crew->number.push_back(temp_ship->get_x() * temp_ship->get_y() + l);
}
temp_crew->number.clear();
}
}
}
end = clock();
cout << "Test 3: " << (end - start) << endl;
return 0;
}

Related

Replacing the value of map elements isnt working

I'm just Learning about std::map and its functions . I recently got this problem. I tried making a program which prints out a grid type data where I used std::map for the keys and values . The program prints out fine but I wanted to make a program where once I erased a data in that grid, other data above that should move down one step and the topmost would have 0 in it . somehow I tried but it doesn't seem to work . I don't know where I did wrong in that . My code:
in class header:
#pragma once
#include<iostream>
#include<string>
#include<vector>
#include<map>
#define x_Pair std::pair<unsigned int,unsigned int>
class MapCheck
{
public:
std::map<x_Pair, unsigned int>m_MapData;
void SetMapData();
x_Pair GetBlockCell(int num);
void EraseCell(int cell);
};
in class cpp:
void MapCheck::SetMapData()
{
int count = 1;
for (int j = 0; j < 20; j++)
{
for (int i = 0; i < 10; i++)
{
m_MapData[{i, j}] = count;
count++;
}
}
}
x_Pair MapCheck::GetBlockCell(int num)
{
for (int j = 0; j < 20; j++)
{
for (int i = 0; i < 10; i++)
{
if (m_MapData[{i, j}] == num)
{
return x_Pair(i, j);
}
}
}
return x_Pair(-1, -1);
}
void MapCheck::EraseCell(int cell)
{
x_Pair pair = GetBlockCell(cell);
for (int i = pair.second; i < 20; i++)
{
m_MapData[{pair.first, i}] = m_MapData[{pair.first, i - 1}];
m_MapData[{pair.first, i - 1}] = 0;
}
}
-and in main:
#include"MapCheck.h"
int main()
{
MapCheck mc;
mc.SetMapData();
std::string input;
do
{
system("cls");
for (int j = 0; j < 20; j++)
{
for (int i = 0; i < 10; i++)
{
std::cout << mc.m_MapData[{i, j}] << " ";
}
std::cout << std::endl;
}
std::cout << "Enter a number to erase or E to exit";
std::cin >> input;
mc.EraseCell(std::atoi(input.c_str()));
} while (input != "E");
return 0;
}
the output without any inputs :
after entering number 191 in the input :
expected result:
All Except the printing is fine . I dont Get Where I did Wrong. Any help would be appreciated. Thanks in advance!!
The order of
for (int i = pair.second; i < 20; i++)
{
m_MapData[{pair.first, i}] = m_MapData[{pair.first, i - 1}];
m_MapData[{pair.first, i - 1}] = 0;
}
is element found to the bottom. When you want to move everything above the item removed down one slot, this isn't all that useful. So lets flip it around.
for (int i = pair.second; i > 0; i--)
{
m_MapData[{pair.first, i}] = m_MapData[{pair.first, i - 1}];
}
m_MapData[{pair.first, 0}] = 0;
This starts at the item being removed and goes up to slot 1, copying each item down one slot. To handle the top of the column, we have m_MapData[{pair.first, 0}] = 0; to set the top item to zero, something we only need to do once.
Side note: Unless we have a sparse array, this would be a lot more efficient with a 2D array in place of the map.
You have following:
x_Pair MapCheck::GetBlockCell(int num)
which is used as
x_Pair pair = GetBlockCell(cell);
This will invoke copy constructor of std::pair<>.
I think you need to return and use reference:
x_Pair& MapCheck::GetBlockCell(int num)
which is used as
x_Pair& pair = GetBlockCell(cell);

read/write to large array using large loop - execution time concerns

So recently I ran into a problem that I thought was interesting and I couldn't fully explain. I've highlighted the nature of the problem in the following code:
#include <cstring>
#include <chrono>
#include <iostream>
#define NLOOPS 10
void doWorkFast(int total, int *write, int *read)
{
for (int j = 0; j < NLOOPS; j++) {
for (int i = 0; i < total; i++) {
write[i] = read[i] + i;
}
}
}
void doWorkSlow(int total, int *write, int *read, int innerLoopSize)
{
for (int i = 0; i < NLOOPS; i++) {
for (int j = 0; j < total/innerLoopSize; j++) {
for (int k = 0; k < innerLoopSize; k++) {
write[j*k + k] = read[j*k + k] + j*k + k;
}
}
}
}
int main(int argc, char *argv[])
{
int n = 1000000000;
int *heapMemoryWrite = new int[n];
int *heapMemoryRead = new int[n];
for (int i = 0; i < n; i++)
{
heapMemoryRead[i] = 1;
}
std::memset(heapMemoryWrite, 0, n * sizeof(int));
auto start1 = std::chrono::high_resolution_clock::now();
doWorkFast(n,heapMemoryWrite, heapMemoryRead);
auto finish1 = std::chrono::high_resolution_clock::now();
auto duration1 = std::chrono::duration_cast<std::chrono::microseconds>(finish1 - start1);
for (int i = 0; i < n; i++)
{
heapMemoryRead[i] = 1;
}
std::memset(heapMemoryWrite, 0, n * sizeof(int));
auto start2 = std::chrono::high_resolution_clock::now();
doWorkSlow(n,heapMemoryWrite, heapMemoryRead, 10);
auto finish2 = std::chrono::high_resolution_clock::now();
auto duration2 = std::chrono::duration_cast<std::chrono::microseconds>(finish2 - start2);
std::cout << "Small inner loop:" << duration1.count() << " microseconds.\n" <<
"Large inner loop:" << duration2.count() << " microseconds." << std::endl;
delete[] heapMemoryWrite;
delete[] heapMemoryRead;
}
Looking at the two doWork* functions, for every iteration, we are reading the same addresses adding the same value and writing to the same addresses. I understand that in the doWorkSlow implementation, we are doing one or two more operations to resolve j*k + k, however, I think it's reasonably safe to assume that relative to the time it takes to do the load/stores for memory read and write, the time contribution of these operations is negligible.
Nevertheless, doWorkSlow takes about twice as long (46.8s) compared to doWorkFast (25.5s) on my i7-3700 using g++ --version 7.5.0. While things like cache prefetching and branch prediction come to mind, I don't have a great explanation as to why doWorkFast is much faster than doWorkSlow. Does anyone have insight?
Thanks
Looking at the two doWork* functions, for every iteration, we are reading the same addresses adding the same value and writing to the same addresses.
This is not true!
In doWorkFast, you index each integer incrementally, as array[i].
array[0]
array[1]
array[2]
array[3]
In doWorkSlow, you index each integer as array[j*k + k], which jumps around and repeats.
When j is 10, for example, and you iterate k from 0 onwards, you are accessing
array[0] // 10*0+0
array[11] // 10*1+1
array[22] // 10*2+2
array[33] // 10*3+3
This will prevent your optimizer from using instructions that can operate on many adjacent integers at once.

Speeding up calculation using vectors in C++ by using pointers/references

Currently, I am making a C++ program that solves a sudoku. In order to do this, I calculate the "energy" of the sudoku (the number of faults) frequently. This calculation unfortunately takes up a lot of computation time. I think that it can be sped up significantly by using pointers and references in the calculation, but have trouble figuring out how to implement this.
In my solver class, I have a vector<vector<int> data-member called _sudoku, that contains the values of each site. Currently, when calculating the energy I call a lot of functions with pass-by-value. I tried adding a & in the arguments of the functions and a * when making the variables, but this did not work. How can I make this program run faster by using pass-by-reference?
Calculating the energy should not change the vector anyway so that would be better.
I used the CPU usage to track down 80% of the calculation time to the function where vectors are called.
int SudokuSolver::calculateEnergy() {
int energy = 243 - (rowUniques() + colUniques() + blockUniques());//count number as faults
return energy;
}
int SudokuSolver::colUniques() {
int count = 0;
for (int col = 0; col < _dim; col++) {
vector<int> colVec = _sudoku[col];
for (int i = 1; i <= _dim; i++) {
if (isUnique(colVec, i)) {
count++;
}
}
}
return count;
}
int SudokuSolver::rowUniques() {
int count = 0;
for (int row = 0; row < _dim; row++) {
vector<int> rowVec(_dim);
for (int i = 0; i < _dim; i++) {
rowVec[i] = _sudoku[i][row];
}
for (int i = 1; i <= _dim; i++) {
if (isUnique(rowVec, i)) {
count++;
}
}
}
return count;
}
int SudokuSolver::blockUniques() {
int count = 0;
for (int nBlock = 0; nBlock < _dim; nBlock++) {
vector<int> blockVec = blockMaker(nBlock);
for (int i = 1; i <= _dim; i++) {
if (isUnique(blockVec, i)) {
count++;
}
}
}
return count;
}
vector<int> SudokuSolver::blockMaker(int No) {
vector<int> block(_dim);
int xmin = 3 * (No % 3);
int ymin = 3 * (No / 3);
int col, row;
for (int i = 0; i < _dim; i++) {
col = xmin + (i % 3);
row = ymin + (i / 3);
block[i] = _sudoku[col][row];
}
return block;
}
bool SudokuSolver::isUnique(vector<int> v, int n) {
int count = 0;
for (int i = 0; i < _dim; i++) {
if (v[i] == n) {
count++;
}
}
if (count == 1) {
return true;
} else {
return false;
}
}
The specific lines that use a lot of computatation time are the ones like:
vector<int> colVec = _sudoku[col];
and every time isUnique() is called.
I expect that if I switch to using pass-by-reference, my code will speed up significantly. Could anyone help me in doing so, if that would indeed be the case?
Thanks in advance.
If you change your SudokuSolver::isUnique to take vector<int> &v, that is the only change you need to do pass-by-reference instead of pass-by-value. Passing with a pointer will be similar to passing by reference, with the difference that pointers could be re-assigned, or be NULL, while references can not.
I suspect you would see some performance increase if you are working on a sufficiently large-sized problem where you would be able to distinguish a large copy (if your problem is small, it will be difficult to see minor performance increases).
Hope this helps!
vector<int> colVec = _sudoku[col]; does copy/transfer all the elements, while const vector<int>& colVec = _sudoku[col]; would not (it only creates an alias for the right hand side).
Same with bool SudokuSolver::isUnique(vector<int> v, int n) { versus bool SudokuSolver::isUnique(const vector<int>& v, int n) {
Edited after Jesper Juhl's suggestion: The const addition makes sure that you don't change the reference contents by mistake.
Edit 2: Another thing to notice is that vector<int> rowVec(_dim); these vectors are continuously allocated and unallocated at each iteration, which might get costly. You could try something like
int SudokuSolver::rowUniques() {
int count = 0;
vector<int> rowVec(_maximumDim); // Specify maximum dimension
for (int row = 0; row < _dim; row++) {
for (int i = 0; i < _dim; i++) {
rowVec[i] = _sudoku[i][row];
}
for (int i = 1; i <= _dim; i++) {
if (isUnique(rowVec, i)) {
count++;
}
}
}
return count;
}
if that doesn't mess up with your implementation.

C++ Program crashes on passing object by value

Error occurs when I pass another object to a function -> a.Concat(b);
I have just reverted to C++ from Java for a project. When I pass object by value in this code, then an error occurs. Almost every time it is displaying a different error message, sometimes bad alloc, sometimes displaying half the output. I tried passing by reference and also made a copy constructor but both attempts failed.
#include<iostream>
#include<vector>
#include<string>
using namespace std;
class NFA
{
public:
NFA(string);
NFA(vector<vector<string> >);
NFA Concat(NFA other_nfa);
NFA Union(NFA);
NFA KleeneStar();
void display();
int GetNFASize(){ return ALPHABET_SIZE; }
int getNoOfStates(){ return NO_OF_STATES; }
vector<vector<string> > table;
int ALPHABET_SIZE;
int NO_OF_STATES;
private:
};
NFA::NFA(string input)
{
table.resize(2);
NO_OF_STATES = 2;
for(int i = 0; i < NO_OF_STATES; i++)
{
table[i].resize(ALPHABET_SIZE + 1);
}
table[0][0] = "2";
table[0][1] = input;
table[1][0] = "3";
ALPHABET_SIZE = 3;
}
void NFA::display()
{
for(int i = 0; i < table.size(); i++)
{
for(int j = 0; j < ALPHABET_SIZE; j++)
{
cout << table[i][j] << "\t";
}
cout << endl;
}
}
NFA NFA::Concat(NFA other_nfa)
{
vector<vector<string> > ans_vector;
ans_vector.resize(ALPHABET_SIZE + other_nfa.ALPHABET_SIZE);
for(int i = 0; i < NO_OF_STATES; i++)
{
for(int j = 0; j < ALPHABET_SIZE; j++)
{
ans_vector[i][j] = table[i][j];
}
}
for(int i = other_nfa.NO_OF_STATES - 1; i < other_nfa.NO_OF_STATES; i++)
{
for(int j = 0; j < other_nfa.ALPHABET_SIZE; j++)
{
ans_vector[i][j] = other_nfa.table[i][j];
}
}
ans_vector[NO_OF_STATES - 1][3] = other_nfa.table[0][0];
NFA ansNFA(ans_vector);
}
NFA::NFA(vector<vector<string> >)
{
}
int main()
{
NFA a("a");
a.display();
NFA b("b");
b.display();
NFA ab = a.Concat(b);
system("pause");
return 0;
}
In
NFA::NFA(string input)
{
table.resize(2);
NO_OF_STATES = 2;
for(int i = 0; i < NO_OF_STATES; i++)
{
table[i].resize(ALPHABET_SIZE + 1);
}
table[0][0] = "2";
table[0][1] = input;
table[1][0] = "3";
ALPHABET_SIZE = 3;
}
You variable initializations are out of order. When you use
table[i].resize(ALPHABET_SIZE + 1);
ALPHABET_SIZE contains garbage as you have not set a value yet. Just move ALPHABET_SIZE = 3; before the for loop and you should be okay.
I would also suggest you use a member initialization list for all variables to you can. In this case your constructor would look like
NFA::NFA(string input) : NO_OF_STATES(2), ALPHABET_SIZE(3)
{
table.resize(2);
for(int i = 0; i < NO_OF_STATES; i++)
{
table[i].resize(ALPHABET_SIZE + 1);
}
table[0][0] = "2";
table[0][1] = input;
table[1][0] = "3";
}
You resize a vector<vector<string> but fail to resize any of the contained vectors:
ans_vector.resize(ALPHABET_SIZE + other_nfa.ALPHABET_SIZE);
And then later you index into the nested vectors, which goes out of bounds:
for(int i = 0; i < NO_OF_STATES; i++)
{
for(int j = 0; j < ALPHABET_SIZE; j++)
{
ans_vector[i][j] = table[i][j];
}
}
You'll need to call resize for every vector inside ans_vector, or use push_back, emplace_back etc, which would probably be safer for you.
AlPHABET_SIZE is not defined in:
table[i].resize(ALPHABET_SIZE + 1);
by default, it contains some garbage value. This might be your problem.
NFA::NFA(string input)
{
table.resize(2);
NO_OF_STATES = 2;
for(int i = 0; i < NO_OF_STATES; i++)
{
table[i].resize(ALPHABET_SIZE + 1);
}
table[0][0] = "2";
table[0][1] = input;
table[1][0] = "3";
ALPHABET_SIZE = 3;
}
ALPHABET_SIZE is being used in the ctor of NFA though it was not initialized. it causes weird behaviour.
You are creating three objects a,b,ab. during construction of the object program crashes. it has nothing to do with pass by reference/value or using copy ctor.
From the first look I would say the problem lays in the vector-access of table, which would go out-of-range.
From design-point, several issues:
It seems you _other_nfa can be declared const reference:
NFA NFA::Concat(const NFA& other_nfa)
There is no return. At the end of your Concat method there should be sth. like:
return ansNFA;
It seems within Concat you don't change your member variables like table (which btw. is no good name for a member-variable). If Concat dosn't change the class members, you should declare it const:
NFA NFA::Concat(const NFA& other_nfa) const

Modulo operation vs. Ternary operation

I need to iterate over n pairs of integers: (0, 1), (1, 2), (2, 3) ... (n-2, n-1), (n-1, 0)
What's the best way to do it?
Using modulo operation:
for (int i = 0; i < n; i++){
int a = i;
int b = (i + 1)%n
//MaaaanyLinesOfDoSomethingWithAAndB
}
Using ternary operation:
for (int i = 0; i < n; i++){
int a = i;
int b = (i + 1 == n ? 0 : i + 1)
//MaaaanyLinesOfDoSomethingWithAAndB
}
Or:
for (int i = 0; i < n; i++){
int a = i;
int b = (i + 1 >= n ? 0 : i + 1)
//MaaaanyLinesOfDoSomethingWithAAndB
}
Another idea? Let's assume that there are maaaany lines of do something and it'd look ugly if we do (0, 1), (1, 2), (2, 3) ... (n-2, n-1) part and (n-1, 0) part separately.
Which operation is the most efficient one?
EDIT #1
I'm sorry, I think I haven't asked my question properly. I wanted to know which operator acts faster (in, e.g. seconds or clock ticks). I also decided to make little experiment and just measure it by clock() function. Here's my code:
#include <time.h>
#include <limits.h>
#include <string>
#include <iostream>
using namespace std;
typedef void (*fun) (int a);
void DoSomething(int i){
int a = i;
}
void ModuloOperation (int n){
for (int i = 0; i < n; i++)
DoSomething((i + 1) % n);
}
void TernaryEqual (int n){
for (int i = 0; i < n; i++)
DoSomething(i + 1 == n ? 0 : i + 1);
}
void TernaryBiggerEqual (int n){
for (int i = 0; i < n; i++)
DoSomething(i + 1 >= n ? 0 : i + 1);
}
void SplitIntoTwoParts (int n){
for (int i = 0; i < n - 1; i++)
DoSomething(i + 1);
DoSomething(n - 1);
}
int main(){
const int n = INT_MAX;
string testNames[] = {
"Modulo",
"Trenary equal",
"Trenary bigger equal",
"Split into two parts"
};
fun tests[] = {
ModuloOperation,
TernaryEqual,
TernaryBiggerEqual,
SplitIntoTwoParts
};
clock_t t;
for (int i = 0; i < sizeof(testNames)/sizeof(testNames[0]); i++){
t = clock();
tests[i](n);
t = clock() - t;
cout<<testNames[i]<<": "<<((float)t)/CLOCKS_PER_SEC<<" seconds\n\n";
}
return 0;
}
And here's an output
Modulo: 53.867 seconds
Trenary equal: 36.684 seconds
Trenary bigger equal: 37.299 seconds
Split into two parts: 31.37 seconds
So it seems that p.s.w.g's idea is not only the cleanest one but also the best one.
And once again, sorry for my mistake, I'm not native speaker, I'm still learning.
You mentioned that it would look ugly if you do the 'maaaany' lines separately. None of the above options are particularly pretty either. So perhaps it's better to encapsulate this ugly logic in a method and use some more elegant code in your loop.
For readability, I'd probably go with this:
for (int i = 0; i < n - 1; i++){
DoStuff(i, i + 1);
}
DoStuff(n - 1, 0);
// elsewhere
void DoStuff(int a, int b)
{
//MaaaanyLinesOfDoSomethingWithAAndB
}
If the 'maaaany' lines require the use of a number local variables and you don't want to pass them all in to the DoStuff method, you might want to consider using a closure, although it wouldn't help the readability of your code nearly as much as proper functional decomposition. Something like this:
Action<int, int> doStuff = (a, b) =>
{
//MaaaanyLinesOfDoSomethingWithAAndB
};
for (int i = 0; i < n - 1; i++){
doStuff(i, i + 1);
}
doStuff(n - 1, 0);
Or perhaps you need to refactor the 'maaaany' lines into a separate worker class, but without knowing more about what's in those lines, it's hard to say.