O que está limitando meu uso de CPU em processos bifurcados?

1

Estou executando alguns programas de longa duração (para propósitos científicos) em uma caixa Linux multi-core. Os processos são controlados por um pequeno daemon que reinicia o próximo trabalho quando um termina (eu corro 3-6 de uma só vez) - mas eu notei que eles nem sempre usam 100% de CPU. Os programas (e o daemon) são escritos em Python.

Quando executo este código no Mac OS X, posso executar programas por semanas e eles sempre usarão tantos recursos do sistema disponíveis, enquanto a máquina estiver funcionando em uma temperatura normal.

Acabei de começar a tentar executar essas coisas no Debian Linux (em outra máquina), com 6 núcleos e muito mais RAM do que os trabalhos precisam. Estou executando 5 desses trabalhos de uma só vez.

Quando iniciei os trabalhos há alguns dias, eu tinha 5 processos Python em top , cada um usando 100% da CPU. Cerca de um dia depois, eu verifiquei como estava tudo e eu tinha 3 processos em 100% da CPU e dois rodando a 50% da CPU. Mais recentemente (cerca de 4 dias) eu tenho 5 processos todos rodando a 20% da CPU.

O que poderia estar causando isso? Nada parece sugerir que as ferramentas de gerenciamento de uso da CPU estão pré-instaladas no Debian Wheezy, e eu não instalei nada parecido (pelo meu conhecimento), nem o configurei. Além disso, como os limites parecem variar dependendo de quanto tempo o demônio está sendo executado, não estou convencido de que possa ser um sistema desse tipo. Eu verifiquei se a máquina está superaquecendo, e não parece ser muito mais quente do que o quarto (frio) está dentro; o ar dos ventiladores / ventiladores é desobstruído e frio.

Os processos ainda estão em execução, para que eu possa medir qualquer coisa que possa ser útil para depuração (tempo de execução, prioridade do processo, etc.) para depurar esse problema. Alguém pode me dizer por onde começar, ou quais possíveis soluções podem ser?

ATUALIZAÇÃO:

Quando eu tento a mesma coisa com 3 threads ao invés de 5, eu caio para 33% cada (depois que a inicial cai para 50%).

Existe algum programa ou política de agendamento que limite todos os processos filhos de um único processo a um total de 100%? como é o que parece estar acontecendo.

O próximo teste é executar diretamente os scripts em screen shells separados, (BTW, o primeiro script foi lançado de dentro de screen ) e ver se temos alguma lentidão. Escalar os trabalhos manualmente é uma solução OK-ish, mas bastante irritante (e deve ser desnecessária). Em geral, é claro, esse tipo de problema pode não ser solucionável dessa maneira, mas como todos os resultados de cada trabalho são salvo no disco ao invés de retornar para o gerenciador de threads, eu vou me safar.

UPDATE 2:

Processos separados iniciados a partir de diferentes instâncias de tela ainda estão a 100% da CPU após 14 horas, reportarão de volta se houver alguma desaceleração, mas, como esperado, este caso não é afetado por nenhum afogamento.

alguém se importa em escrever (ou apontar para mim) algo que explica a prioridade do processo no Linux? Eu estou querendo saber se meus processos de desova estão sendo marcados como uma prioridade mais baixa (já que ele usa muito pouca CPU), e então os processos filhos estão herdando isso.

EDITAR:

Fui questionado sobre o script que estou executando e a função de bifurcar os processos do daemon.

O script de longa execução é um grande cálculo, que sempre é executado com 100% da CPU até terminar e não faz nada engraçado sobre a paralelização ou o multiprocessamento. (isso é uma afirmação amplamente testada.) Para esclarecer ainda mais - as únicas vezes em que vi esses processos sendo executados em menos de 100% da CPU no meu Mac estão superaquecendo ou quando paginando / trocando. Nenhum destes é relevante para o caso do Linux.

Aqui está a função que se bifurca e gerencia os processos de longa duração:

from multiprocessing import Process
import time, sys, os

# An alternative entry point which runs N jobs in parallel over a list of files.
# Note, since this is designed to be used as a library function, we "return" from the initial
# function call rather than exiting.
def run_over_file_list(script_path, list_of_data_files, num_processes, timeout=float('inf')):
    try:
        pid = os.fork()
        if pid > 0:
            # exit first parent
            return
    except OSError, e:
        print >>sys.stderr, "fork #1 failed: %d (%s)" % (e.errno, e.strerror)
        sys.exit(1)

    # decouple from parent environment
    os.chdir("/")
    os.setsid()
    os.umask(0)

    # do second fork
    try:
        pid = os.fork()
        if pid > 0:
            # exit from second parent, print eventual PID before
            print "Daemon PID %d" % pid
            sys.exit(0)
    except OSError, e:
        print >>sys.stderr, "fork #2 failed: %d (%s)" % (e.errno, e.strerror)
        sys.exit(1)

    # OK, we're inside a manager daemon.
    if os.path.isfile(status_filename):
        raise Exception("a daemon is already running. failed.")

    f = open(status_filename, "w")
    f.write(str(os.getpid()))
    f.close()

    jobs = [script_path] * num_processes
    data_files_remaining = [f for f in list_of_data_files]
    update_files_remaining_file(len(data_files_remaining))

    assert num_processes <= len(data_files_remaining)

    restart = False

    with nostdout():
        while True:
            processes = []

            for job in jobs:
                p = Process(target=file_list_worker, args=(job, data_files_remaining.pop(0)))
                p.started = time.time()
                p.start()
                processes.append(p)

            stop = False
            while True:
                time.sleep(10)

                ended = []
                for i, p in enumerate(processes):
                    if not p.is_alive():
                        j = i
                        ended.append((j,p))
                    elif time.time() - p.started > timeout:
                        p.terminate()
                        j = i
                        ended.append((j,p))
                if not stop:
                    for tup in ended:
                        if not data_files_remaining:
                            stop = True
                            break
                        i, e = tup
                        new_p = Process(target=file_list_worker, args=(jobs[i], data_files_remaining.pop(0)))
                        new_p.started = time.time()
                        new_p.start()
                        processes[i] = new_p
                        # old e will be garbage collected
                else:
                    if len(ended) == len(processes) and not data_files_remaining:
                        stop = False
                        break

                try:
                    command = check_for_command()
                    if command == "stop":
                        stop = True
                    elif command == "restart":
                        stop = True
                        restart = True
                    elif command == "kill":
                        for p in processes:
                            p.terminate()
                        clear_command()
                        os.remove(status_filename)
                        exit(0)
                except NoCommandError:
                    pass

                update_files_remaining_file(len(data_files_remaining))

            clear_command()

            update_files_remaining_file(len(data_files_remaining))

            if not restart:
                os.remove(status_filename)
                break
            else:
                jobs = None
                restart = False

    # While in a fork, we should never return (will continue running the original/calling script in parallel, hilarity ensues.)
    exit(0)

EDIT 2:

prioridade

Assim, tudo parece rodar com prioridade 20 de qualquer fonte; os processos pré-regulados, os processos pós-regulados, o gerenciador de daemon, os processos executados diretamente do shell sob a tela.

ulimit -a

do bash:

core file size          (blocks, -c) 0
data seg size           (kbytes, -d) unlimited
scheduling priority             (-e) 0
file size               (blocks, -f) unlimited
pending signals                 (-i) 127788
max locked memory       (kbytes, -l) 64
max memory size         (kbytes, -m) unlimited
open files                      (-n) 1024
pipe size            (512 bytes, -p) 8
POSIX message queues     (bytes, -q) 819200
real-time priority              (-r) 0
stack size              (kbytes, -s) 8192
cpu time               (seconds, -t) unlimited
max user processes              (-u) 127788
virtual memory          (kbytes, -v) unlimited
file locks                      (-x) unlimited

de peixe:

Maximum size of core files created                           (kB, -c) 0
Maximum size of a process’s data segment                     (kB, -d) unlimited
Maximum size of files created by the shell                   (kB, -f) unlimited
Maximum size that may be locked into memory                  (kB, -l) 64
Maximum resident set size                                    (kB, -m) unlimited
Maximum number of open file descriptors                          (-n) 1024
Maximum stack size                                           (kB, -s) 8192
Maximum amount of cpu time in seconds                   (seconds, -t) unlimited
Maximum number of processes available to a single user           (-u) 127788
Maximum amount of virtual memory available to the shell      (kB, -v) unlimited

de peixes em tela:

(exatamente o mesmo que peixe normal.)

Mais tarde, atualizar

Eu também notei esse bug com processos de execução longa executados a partir de shells separados. por exemplo:

Instance 1: 17% (one core of 6 at 100%.)
Instance 2: 8% (one core of 6 at  50%.)
Instance 3: 8% (one core of 6 at  50%.)

se eu alterar a prioridade da instância 2 para "muito alta", o estado se tornará:

Instance 1: 17% (one core of 6 at 100%.)
Instance 2: 17% (one core of 6 at 100%.)
Instance 3: 0% (one core of 6 at    0%.)

Se as prioridades forem todas equalizadas novamente, retornamos ao primeiro estado.

Estou começando a pensar que esse problema pode estar relacionado a uma configuração de hardware específica ou algo assim, mas não tenho as ferramentas / conhecimento para depurar ainda mais.

    
por tehwalrus 15.09.2014 / 13:52

0 respostas

Tags