Сценарий моего приложения такой: я хочу оценить прирост производительности, который можно получить на четырехъядерном компьютере для обработки того же объема данных. У меня есть следующие две конфигурации:
i) 1-процесс: программа без потоков и обрабатывает данные из 1M .. 1G, в то время как система должна была работать только на одном ядре из 4-ядерных.
ii) Процесс с 4 потоками: программа с 4 потоками (все потоки выполняют одну и ту же операцию), но обрабатывает 25% входных данных.
В моей программе для создания 4-х потоков я использовал параметры по умолчанию для pthread (т.е. без какого-либо конкретного pthread_attr_t). Я полагаю, что прирост производительности 4-потоковой конфигурации по сравнению с 1-процессной конфигурацией должен быть ближе к 400% (или где-то между 350% и 400%).
Я профилировал время, потраченное на создание потоков, как показано ниже:
timer_start(&threadCreationTimer);
pthread_create( &thread0, NULL, fun0, NULL );
pthread_create( &thread1, NULL, fun1, NULL );
pthread_create( &thread2, NULL, fun2, NULL );
pthread_create( &thread3, NULL, fun3, NULL );
threadCreationTime = timer_stop(&threadCreationTimer);
pthread_join(&thread0, NULL);
pthread_join(&thread1, NULL);
pthread_join(&thread2, NULL);
pthread_join(&thread3, NULL);
Поскольку увеличение размера входных данных может также увеличить требования к памяти для каждого потока, то загрузка всех данных заранее - определенно нереальный вариант. Следовательно, чтобы не увеличивать требования к памяти для каждого потока, каждый поток считывает данные небольшими порциями, обрабатывает их и считывает следующий блок обработки, и так далее. Следовательно, структура кода моих функций, выполняемых потоками, выглядит следующим образом:
timer_start(&threadTimer[i]);
while(!dataFinished[i])
{
threadTime[i] += timer_stop(&threadTimer[i]);
data_source();
timer_start(&threadTimer[i]);
process();
}
threadTime[i] += timer_stop(&threadTimer[i]);
Переменная dataFinished[i]
помечается true
процессом, когда она получает и обрабатывает все необходимые данные. Process()
знает, когда это сделать: -)
В основной функции я рассчитываю время, затрачиваемое на 4-х поточную конфигурацию, как показано ниже:
execTime4Thread = max(threadTime[0], threadTime[1], threadTime[2], threadTime[3]) + threadCreationTime
.
А прирост производительности вычисляется просто
gain = execTime1process / execTime4Thread * 100
Проблема:
При небольшом размере данных от 1 до 4 млн. Прирост производительности в целом хороший (от 350 до 400%). Однако тенденция увеличения производительности экспоненциально уменьшается с увеличением размера входных данных. Он продолжает уменьшаться до некоторого размера данных до 50M или около того, а затем становится стабильным около 200%. По достижении этой точки он остается практически стабильным даже для 1 ГБ данных.
Мой вопрос: может ли кто-нибудь предложить основную причину такого поведения (то есть падение производительности в начале, но в дальнейшем оно останется стабильным)?
И предложения как это исправить?
Для вашего сведения, я также исследовал поведение threadCreationTime
и threadTime
для каждого потока, чтобы увидеть, что происходит. Для 1М данных значения этих переменных малы, но с увеличением размера данных обе эти переменные растут экспоненциально (но threadCreationTime
должно оставаться почти одинаковым независимо от размера данных, а threadTime
должно увеличиваться со скоростью, соответствующей данным в процессе обработки). После продолжения увеличения до тех пор, пока 50M или около того threadCreationTime
не станет стабильным и threadTime
(точно так же, как падение производительности становится стабильным), и threadCreationTime
продолжают увеличиваться с постоянной скоростью, соответствующей увеличению данных, которые будут обрабатываться (что считается понятным).
Как вы думаете, может помочь увеличение размера стека каждого потока, приоритета процесса или пользовательских значений другого типа планировщика (с использованием pthread_attr_init
)?
PS: результаты получаются при запуске программ в отказоустойчивом режиме Linux с рутом (то есть минимальная ОС работает без графического интерфейса и сетевых ресурсов).