Вопрос

Для одного из проектов, над которым я сейчас работаю, мне нужно посмотреть на производительность (среди прочего) различных одновременный включен языки программирования.

На данный момент я пытаюсь сравнить бесстековый питон и C++ PThreads, поэтому основное внимание уделяется этим двум языкам, но другие языки, вероятно, будут протестированы позже.Конечно, сравнение должно быть максимально репрезентативным и точным, поэтому моей первой мыслью было начать искать какой-то стандарт. проблемы с параллельными/многопоточными тестами, Увы, я не смог найти достойных или стандартных тестов/проблем/бенчмарков.

Итак, мой вопрос заключается в следующем: Есть ли у вас предложение по хорошей, простой или быстрой задаче для проверки производительности языка программирования? (и при этом выявить его сильные и слабые стороны)?

Это было полезно?

Решение

После того, как в сентябре 2008 года игра с тестами перешла на четырехъядерный компьютер, многие программы на разных языках программирования были переписаны для использования четырехъядерного процессора - например, первые 10 программ Мандельброта .

Другие советы

Конечно, вы должны тестировать аппаратное обеспечение и компиляторы, а не язык для производительности параллелизма?

Я бы посмотрел на язык с точки зрения того, насколько он прост и продуктивен с точки зрения параллелизма и насколько он «изолирует» программиста от ошибок блокировки.

РЕДАКТИРОВАТЬ: исходя из прошлого опыта исследователя, разрабатывающего параллельные алгоритмы, я думаю, вы обнаружите, что в большинстве случаев параллельная производительность будет зависеть в значительной степени от того, как распараллеливается алгоритм и как он нацелен на базовое оборудование.

Кроме того, тесты, как известно, неравны; это тем более в параллельной среде. Например, эталонный тест, который «хрустит» очень большие матрицы, будет подходить для векторного конвейерного процессора, тогда как параллельная сортировка может быть лучше приспособлена для многоядерных процессоров более общего назначения.

Это может быть полезно:

Параллельные тесты

Параллельные тесты NAS

Ну, есть немного классики, но разные тесты подчеркивают разные особенности. Некоторые распределенные системы могут быть более устойчивыми, иметь более эффективную передачу сообщений и т. Д. Более высокая нагрузка на сообщения может отрицательно сказаться на масштабируемости, поскольку обычный способ масштабирования до большего числа машин - отправка большего количества небольших сообщений. Некоторые классические проблемы, которые вы можете попробовать, - это распределенное сито Эратосфена или плохо реализованный калькулятор последовательности Фибоначчи (то есть для вычисления 8-го числа в серии, вращения машины для 7-го и другого для 6-го). Практически любой алгоритм «разделяй и властвуй» может быть выполнен одновременно. Вы также можете выполнить параллельную реализацию игры Конвея о жизни или теплопередаче. Обратите внимание, что все эти алгоритмы имеют различную направленность, и, следовательно, вы, вероятно, не добьетесь того, чтобы одна распределенная система работала наилучшим образом во всех из них.

Я бы сказал, что самый простой для реализации - это плохо реализованный калькулятор Фибоначчи, хотя он слишком акцентирует внимание на создании потоков и слишком мало на связи между этими потоками.

Конечно, вам следует тестировать аппаратное обеспечение и компиляторы, а не язык для выполнения параллелизма?

Нет, оборудование и компиляторы не имеют значения для моих целей тестирования.Я просто ищу хорошие задачи, которые помогут проверить, насколько хорошо код, написанный на одном языке, может конкурировать с кодом на другом языке.Я действительно тестирую конструкции, доступные на конкретных языках, для параллельного программирования.И один из критериев — производительность (измеренная во времени).

Некоторые из других критериев тестирования, которые я ищу:

  • как легкий нужно ли писать правильный код;потому что, как мы все знаем, параллельное программирование сложнее, чем написание однопоточных программ.
  • какая техника используется для параллельного программирования:управляемый событиями, на основе актеров, анализ сообщений, ...
  • сколько кода должен написать сам программист и сколько делается за него автоматически:это также можно проверить с помощью заданных эталонных задач
  • каков уровень абстракции и сколько накладных расходов требуется при обратном переводе в машинный код

Так что на самом деле я не ищу производительности, поскольку тот единственный и лучший параметр (который действительно отправит меня к аппаратному обеспечению и компиляторам, а не к самому языку), я на самом деле смотрю с точки зрения программиста, чтобы проверить, какой язык лучше всего подходит для каких проблем, какие у него слабые стороны и сильные стороны и так далее...

Имейте в виду, что это всего лишь небольшой проект, и поэтому тесты также должны быть небольшими.(поэтому тщательное тестирование всего невозможно)

Я решил использовать набор Мандельброта ( алгоритм времени выхода , чтобы быть более точным) для сравнения различных языков.
Он подходит мне очень хорошо, так как оригинальный алгоритм может быть легко реализован, и создание многопоточного варианта не так уж много работы.

ниже приведен код, который у меня есть. Это все еще однопоточный вариант, но я обновлю его, как только я буду удовлетворен результатом.

#include <cstdlib> //for atoi
#include <iostream>
#include <iomanip> //for setw and setfill
#include <vector>


int DoThread(const double x, const double y, int maxiter) {
    double curX,curY,xSquare,ySquare;
    int i;

    curX = x + x*x - y*y;
    curY = y + x*y + x*y;
    ySquare = curY*curY;
    xSquare = curX*curX;

    for (i=0; i<maxiter && ySquare + xSquare < 4;i++) {
      ySquare = curY*curY;
      xSquare = curX*curX;
      curY = y + curX*curY + curX*curY;
      curX = x - ySquare + xSquare;
    }
    return i;
}

void SingleThreaded(int horizPixels, int vertPixels, int maxiter, std::vector<std::vector<int> >&  result) {
    for(int x = horizPixels; x > 0; x--) {
        for(int y = vertPixels; y > 0; y--) {
            //3.0 -> so we always have -1.5 -> 1.5 as the window; (x - (horizPixels / 2) will go from -horizPixels/2 to +horizPixels/2
            result[x-1][y-1] = DoThread((3.0 / horizPixels) * (x - (horizPixels / 2)),(3.0 / vertPixels) * (y - (vertPixels / 2)),maxiter);
        }
    }
}

int main(int argc, char* argv[]) {
    //first arg = length along horizontal axis
    int horizPixels = atoi(argv[1]);

    //second arg = length along vertical axis
    int vertPixels = atoi(argv[2]);

    //third arg = iterations
    int maxiter = atoi(argv[3]);

    //fourth arg = threads
    int threadCount = atoi(argv[4]);

    std::vector<std::vector<int> > result(horizPixels, std::vector<int>(vertPixels,0)); //create and init 2-dimensional vector
    SingleThreaded(horizPixels, vertPixels, maxiter, result);

    //TODO: remove these lines
    for(int y = 0; y < vertPixels; y++) {
      for(int x = 0; x < horizPixels; x++) {
            std::cout << std::setw(2) << std::setfill('0') << std::hex << result[x][y] << " ";
        }
        std::cout << std::endl;
    }
}

Я протестировал его с gcc под Linux, но уверен, что он работает и под другими компиляторами / операционными системами. Чтобы заставить его работать, вы должны ввести несколько аргументов командной строки, например:

  

Мандельброт 106 500 255 1

первый аргумент - ширина (ось X)
второй аргумент - высота (ось Y)
третий аргумент - количество максимальных итераций (количество цветов)
последние включения - это количество потоков (но это в настоящее время не используется)

В моем разрешении приведенный выше пример дает мне хорошее представление ASCII-набора Мандельброта. Но попробуйте сами с другими аргументами (первый будет самым важным, так как это будет ширина)

Ниже вы можете найти код, который я взломал вместе, чтобы проверить многопоточность производительности pthreads. Я не убрал это, и никакие оптимизации не были сделаны; поэтому код немного raw .

код для сохранения вычисленного набора Мандельброта как растрового изображения не мой, его можно найти здесь

#include <cstdlib> //for atoi
#include <iostream>
#include <iomanip> //for setw and setfill
#include <vector>

#include "bitmap_Image.h" //for saving the mandelbrot as a bmp

#include <pthread.h>

pthread_mutex_t mutexCounter;
int sharedCounter(0);
int percent(0);

int horizPixels(0);
int vertPixels(0);
int maxiter(0);

//doesn't need to be locked
std::vector<std::vector<int> > result; //create 2 dimensional vector

void *DoThread(void *null) {
    double curX,curY,xSquare,ySquare,x,y;
    int i, intx, inty, counter;
    counter = 0;

    do {
        counter++;
        pthread_mutex_lock (&mutexCounter); //lock
            intx = int((sharedCounter / vertPixels) + 0.5);
            inty = sharedCounter % vertPixels;
            sharedCounter++;
        pthread_mutex_unlock (&mutexCounter); //unlock

        //exit thread when finished
        if (intx >= horizPixels) {
            std::cout << "exited thread - I did " << counter << " calculations" << std::endl;
            pthread_exit((void*) 0);
        }

        //set x and y to the correct value now -> in the range like singlethread
        x = (3.0 / horizPixels) * (intx - (horizPixels / 1.5));
        y = (3.0 / vertPixels) * (inty - (vertPixels / 2));

        curX = x + x*x - y*y;
        curY = y + x*y + x*y;
        ySquare = curY*curY;
        xSquare = curX*curX;

        for (i=0; i<maxiter && ySquare + xSquare < 4;i++){
          ySquare = curY*curY;
          xSquare = curX*curX;
          curY = y + curX*curY + curX*curY;
          curX = x - ySquare + xSquare;
        }
        result[intx][inty] = i;
     } while (true);
}

int DoSingleThread(const double x, const double y) {
    double curX,curY,xSquare,ySquare;
    int i;

    curX = x + x*x - y*y;
    curY = y + x*y + x*y;
    ySquare = curY*curY;
    xSquare = curX*curX;

    for (i=0; i<maxiter && ySquare + xSquare < 4;i++){
      ySquare = curY*curY;
      xSquare = curX*curX;
      curY = y + curX*curY + curX*curY;
      curX = x - ySquare + xSquare;
    }
    return i;

}

void SingleThreaded(std::vector<std::vector<int> >&  result) {
    for(int x = horizPixels - 1; x != -1; x--) {
        for(int y = vertPixels - 1; y != -1; y--) {
            //3.0 -> so we always have -1.5 -> 1.5 as the window; (x - (horizPixels / 2) will go from -horizPixels/2 to +horizPixels/2
            result[x][y] = DoSingleThread((3.0 / horizPixels) * (x - (horizPixels / 1.5)),(3.0 / vertPixels) * (y - (vertPixels / 2)));
        }
    }
}

void MultiThreaded(int threadCount, std::vector<std::vector<int> >&  result) {
    /* Initialize and set thread detached attribute */
    pthread_t thread[threadCount];
    pthread_attr_t attr;
    pthread_attr_init(&attr);
    pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_JOINABLE);


    for (int i = 0; i < threadCount - 1; i++) {
        pthread_create(&thread[i], &attr, DoThread, NULL);
    }
    std::cout << "all threads created" << std::endl;

    for(int i = 0; i < threadCount - 1; i++) {
        pthread_join(thread[i], NULL);
    }
    std::cout << "all threads joined" << std::endl;
}

int main(int argc, char* argv[]) {
    //first arg = length along horizontal axis
    horizPixels = atoi(argv[1]);

    //second arg = length along vertical axis
    vertPixels = atoi(argv[2]);

    //third arg = iterations
    maxiter = atoi(argv[3]);

    //fourth arg = threads
    int threadCount = atoi(argv[4]);

    result = std::vector<std::vector<int> >(horizPixels, std::vector<int>(vertPixels,21)); // init 2-dimensional vector
    if (threadCount <= 1) {
        SingleThreaded(result);
    } else {
        MultiThreaded(threadCount, result);
    }


    //TODO: remove these lines
    bitmapImage image(horizPixels, vertPixels);
    for(int y = 0; y < vertPixels; y++) {
      for(int x = 0; x < horizPixels; x++) {
            image.setPixelRGB(x,y,16777216*result[x][y]/maxiter % 256, 65536*result[x][y]/maxiter % 256, 256*result[x][y]/maxiter % 256);
            //std::cout << std::setw(2) << std::setfill('0') << std::hex << result[x][y] << " ";
        }
        std::cout << std::endl;
    }

    image.saveToBitmapFile("~/Desktop/test.bmp",32);
}

хорошие результаты можно получить с помощью программы со следующими аргументами:

  

Мандельброт 5120 3840 256 3

таким образом вы получите изображение шириной 5 * 1024; Высота 5 * 768 с 256 цветами (увы, вы получите только 1 или 2) и 3 потока (1 основной поток, который не выполняет никакой работы, кроме создания рабочих потоков, и 2 рабочих потока)

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top