Augeaz
Augeaz

Reputation: 121

Is there a better way of finding solution than using nested loops?

I am looking to speed up the process of testing all the combinations based on the nested loops in the piece of code included.

I am currently stuck with JavaScript and NodeJS on Windows 10.

Is there a way to compute this using GPU rather than CPU?

var simulations = 0;
for (let i = 10; i <= 20; i++) {
    breakStepThree = i;
    for (let i = 8; i <= 12; i++) {
        historyLevelThree = i;
        for (let i = 0; i <= 60; i += 5) {
            rateLevelThree = i;
            for (let i = 10; i <= 16; i++) {
                breakStepTwo = i;
                for (let i = 6; i <= 10; i++) {
                    historyLevelTwo = i;
                    for (let i = 0; i <= 50; i += 5) {
                        rateLevelTwo = i;
                        for (let i = 10; i <= 14; i++) {
                            breakStepOne = i;
                            for (let i = 4; i <= 8; i++) {
                                historyLevelOne = i;
                                for (let i = 0; i <= 40; i += 5) {
                                    rateLevelOne = i;
                                    simulations++;
                                    console.log('testing combination '
                                        + rateLevelOne + ' ' + historyLevelOne + ' ' + breakStepOne + ' '
                                        + rateLevelTwo + ' ' + historyLevelTwo + ' ' + breakStepTwo + ' '
                                        + rateLevelThree + ' ' + historyLevelThree + ' ' + breakStepThree
                                    );
                                    console.log('performing test no ' + simulations);
                                }
                            }
                        }
                    }
                }
            }
        }
    }
}

Implementation of worker threads to the best of my ability.

const {
    Worker, isMainThread, parentPort, workerData
} = require('worker_threads');

const os = require('os');

if (isMainThread) {
    const startTime = Date.now();

    const workers = [];

    // const numberOfThreads = 1;
    const numberOfThreads = os.cpus().length;

    let completed = 0;

    let minBreakStep = 10;
    let maxBreakStep = 20;
    let minMaxElements = [];

    for (let i = minBreakStep; i <= maxBreakStep; i++) {
        minMaxElements.push(i);
    }

    const numberOfElements = minMaxElements.length;

    const numElementsPerThread = Math.ceil(numberOfElements / numberOfThreads);

    let workerIndex = 0;

    let allSimulations = 0;

    for (let i = minBreakStep; i <= maxBreakStep; i += numElementsPerThread) {
        let workerStart = i;
        let workerEnd = i + numElementsPerThread - 1;
        if (workerEnd > maxBreakStep) {
            workerEnd = maxBreakStep
        }

        const worker = new Worker(__filename, {
            workerData: {
                workerIndex,
                workerStart,
                workerEnd,
            }
        });

        worker.on('message', (message) => {
            if (message.completed) {
                completed++;
                console.log('worker ' + message.workerIndex + ' completed ' + message.simulations + ' simulations.');
                allSimulations += message.simulations;
            }
            if (completed === workers.length) {
                console.log('Completed all ' + allSimulations + ' done!');
                const endTime = Date.now();
                const elapsedTime = (endTime - startTime) / 1000;
                console.log(elapsedTime + ' second(s) to complete');
            }
        });
        workerIndex++;
        workers.push(worker);
    }

} else {

    let workerIndex = workerData.workerIndex;
    let workerStart = workerData.workerStart;
    let workerEnd = workerData.workerEnd;

    let simulations = 0;

    for (let i = workerStart; i <= workerEnd; i++) {
        breakStepThree = i;
        for (let i = 8; i <= 12; i++) {
            historyLevelThree = i;
            for (let i = 0; i <= 60; i += 5) {
                rateLevelThree = i;
                for (let i = 10; i <= 16; i++) {
                    breakStepTwo = i;
                    for (let i = 6; i <= 10; i++) {
                        historyLevelTwo = i;
                        for (let i = 0; i <= 50; i += 5) {
                            rateLevelTwo = i;
                            for (let i = 10; i <= 14; i++) {
                                breakStepOne = i;
                                for (let i = 4; i <= 8; i++) {
                                    historyLevelOne = i;
                                    for (let i = 0; i <= 40; i += 5) {
                                        rateLevelOne = i;
                                        simulations++;
                                        // console.log('testing combination '
                                        //     + rateLevelOne + ' ' + historyLevelOne + ' ' + breakStepOne + ' '
                                        //     + rateLevelTwo + ' ' + historyLevelTwo + ' ' + breakStepTwo + ' '
                                        //     + rateLevelThree + ' ' + historyLevelThree + ' ' + breakStepThree
                                        // );
                                        // console.log('performing test no ' + simulations);
                                    }
                                }
                            }
                        }
                    }
                }
            }
        }
    }

    console.log('testing completed');
    parentPort.postMessage({
        completed: true,
        workerIndex: workerIndex,
        simulations: simulations,
    });
}

Upvotes: 0

Views: 105

Answers (3)

Trott
Trott

Reputation: 70055

In Node.js, I'm unaware of any easy to use the GPU. Depending on the nature of the work being done to test each combination and the characteristics of your host system, you can use the built-in worker_threads and/or cluster modules to spread out the work. The worker_threads module creates threads within a process. The cluster module creates separate processes.

Upvotes: 1

Augeaz
Augeaz

Reputation: 121

Implementation of worker threads to the best of my ability.

const {
    Worker, isMainThread, parentPort, workerData
} = require('worker_threads');

const os = require('os');

if (isMainThread) {
    const startTime = Date.now();

    const workers = [];

    // const numberOfThreads = 1;
    const numberOfThreads = os.cpus().length;

    let completed = 0;

    let minBreakStep = 10;
    let maxBreakStep = 20;
    let minMaxElements = [];

    for (let i = minBreakStep; i <= maxBreakStep; i++) {
        minMaxElements.push(i);
    }

    const numberOfElements = minMaxElements.length;

    const numElementsPerThread = Math.ceil(numberOfElements / numberOfThreads);

    let workerIndex = 0;

    let allSimulations = 0;

    for (let i = minBreakStep; i <= maxBreakStep; i += numElementsPerThread) {
        let workerStart = i;
        let workerEnd = i + numElementsPerThread - 1;
        if (workerEnd > maxBreakStep) {
            workerEnd = maxBreakStep
        }

        const worker = new Worker(__filename, {
            workerData: {
                workerIndex,
                workerStart,
                workerEnd,
            }
        });

        worker.on('message', (message) => {
            if (message.completed) {
                completed++;
                console.log('worker ' + message.workerIndex + ' completed ' + message.simulations + ' simulations.');
                allSimulations += message.simulations;
            }
            if (completed === workers.length) {
                console.log('Completed all ' + allSimulations + ' done!');
                const endTime = Date.now();
                const elapsedTime = (endTime - startTime) / 1000;
                console.log(elapsedTime + ' second(s) to complete');
            }
        });
        workerIndex++;
        workers.push(worker);
    }

} else {

    let workerIndex = workerData.workerIndex;
    let workerStart = workerData.workerStart;
    let workerEnd = workerData.workerEnd;

    let simulations = 0;

    for (let i = workerStart; i <= workerEnd; i++) {
        breakStepThree = i;
        for (let i = 8; i <= 12; i++) {
            historyLevelThree = i;
            for (let i = 0; i <= 60; i += 5) {
                rateLevelThree = i;
                for (let i = 10; i <= 16; i++) {
                    breakStepTwo = i;
                    for (let i = 6; i <= 10; i++) {
                        historyLevelTwo = i;
                        for (let i = 0; i <= 50; i += 5) {
                            rateLevelTwo = i;
                            for (let i = 10; i <= 14; i++) {
                                breakStepOne = i;
                                for (let i = 4; i <= 8; i++) {
                                    historyLevelOne = i;
                                    for (let i = 0; i <= 40; i += 5) {
                                        rateLevelOne = i;
                                        simulations++;
                                        // console.log('testing combination '
                                        //     + rateLevelOne + ' ' + historyLevelOne + ' ' + breakStepOne + ' '
                                        //     + rateLevelTwo + ' ' + historyLevelTwo + ' ' + breakStepTwo + ' '
                                        //     + rateLevelThree + ' ' + historyLevelThree + ' ' + breakStepThree
                                        // );
                                        // console.log('performing test no ' + simulations);
                                    }
                                }
                            }
                        }
                    }
                }
            }
        }
    }

    console.log('testing completed');
    parentPort.postMessage({
        completed: true,
        workerIndex: workerIndex,
        simulations: simulations,
    });
}

Upvotes: 1

Kilian
Kilian

Reputation: 1939

You can for example use CUDA bindings to perform calculations on GPU. Maybe in this case it would be better to interface a C program and import/export the experimental design and results. https://github.com/kashif/node-cuda / https://www.npmjs.com/package/cuda-node-js

Additionally I would get rid of all these nested loops with something like below. For performance sake maybe get rid of the slicing, and check the parameters as the few lines below are just to get the idea across.

class ExperimentGenerator{

  let pointer = 0;
  const initialParams = [0,0,0,0,0,0,0,0];
  let params = [...initialParams]
  const maxValue = [10,10,10,10,10,10,10,10]

  nextExperiment = () =>{
     if(params[pointer] >= maxValue[pointer]){
         pointer++;
         //Reset all other parameters
         params = [initialParams.slice(0,pointer),params[pointer],initialParams.slice(pointer)];
     }
     params[pointer]++;
     return params;
  }
}

Upvotes: 0

Related Questions