Jump to content

Threads ou multprocesso?


jv.batista
 Share

Recommended Posts

Viva,

Qual das duas opções é a melhor para correr varias funções ao mesmo tempo?

Neste momento tenho várias threads a serem criadas consoante o numero de cpus(ver código abaixo) mas para além de não dever ser bem aquilo que quero a segunda só é criada depois de acabada a função que está na primeira thread. A função que está a ser chamada no exemplo é bastante rápida e até pode dispensar a utilização de threads/processos, no entanto as outras duas que tenho de usar já vão demorar bastante( actualmente, a segunda função, está a correr em cerca de 8h se não me engano ).

Alguém sabe como é que posso resolver isto?

Cumprimentos

jv.batista

edit: espero que não tenha confundido o pessoal. O que encontrei no forum era para C...


class CreateAllStatistics(threading.Thread):


    objt = None


    thread = None
    
    cpuN = multiprocessing.cpu_count()


    forRange = {1 : [2000, 2011],
                2 : [2000, 2005, 2011],
                3 : [2000, 2003, 2007, 2011],
                4 : [2000, 2002, 2005, 2008, 2011]}


    if(cpuN > 4):
        cpuN = 4
        pass


    print cpuN
        


    def stop (self):
        global thread
        thread.kill()
        print "thread stopped"
        pass
    pass
    
    def start_thread (self):
        global thread
        for x in range (0, cpuN):
            print 'thread ' + str(x) + ' created!\n'
            initValue = forRange[cpuN][x]
            endValue = forRange[cpuN][x + 1]
            thread = KThread(target=self.start_cicle(initValue, endValue))
            thread.start()
        pass
    pass


    def start_cicle(self, initValue, endValue):
        global objt


        objt = getStatistics()
        schoolQuantityTemp = {}
        
        for year in range(initValue, endValue):
            connection = sqlite3.connect('RebidesDBMulti.db')
            c = connection.cursor()
            c.execute(''' SELECT * FROM prof_data_year_{0} '''.format(year))
            databaseContent = c.fetchall()
            c.close()
            schoolQuantityTemp[year] = (objt.getSchoolNamePerYear(year, databaseContent))
            print len(schoolQuantityTemp[year])
            pass 
        #self.stop()
        pass
    pass


    def printResults(self):
        global schoolQuantity


        for key in schoolQuantity:
            print len(schoolQuantity[key])
            pass
        pass

Link to comment
Share on other sites

Vê se o link referido neste tópico que ajuda em alguma coisa: http://www.portugal-a-programar.pt/index.php?showtopic=37608

Não sei se o sistema se mantém no 3.0.

estou a usar o 2.7.

Confirma-se a minha suspeição, vai ter mesmo de ser multiprocessing. Já estive de volta daquilo mas o idle acaba por crashar quando tentei criar 3 processos para fazer uma função simples que ia fazer um print do numero do processo umas quantas vezes...

Cumps

Link to comment
Share on other sites

Boas!

Antes de começar, uma dica para a apresentação do post: Utiliza a ferramenta GeSHi para apresentares o teu código.

Para o que queres fazer, eu aconcelho-te utilizares o modulo threading e esqueceres o multiprocessing.

Bem, o teu código está um bocado confuso... Devias dar uma leitura num manual de python, funcionamento de classes, etc para melhor perceberes como vai funcionar o teu código uma vez que estás a derivar a tua classe.

Ora bem, não sei ate que ponto faz sentido utilizares o número de CPU's existentes, uma vez que utilizando o modulo threading o "trabalho" vai ser distribuido automaticamente consuate os recursos. Portanto vamos deixar a parte do número de CPU's de parte.

A estrutura que precisas é algo deste genero:

class NomeAqui( threading.Thread ):
    def  __init__( self ):
        # Definir aqui todos os objectos e atributos da classe

    def run( self ):
        # Colocar aqui o código que queres que a thread execute durante o seu tempo de vida




if ( __name__ == "__main__" ):
    # Número de threads que queres criar
    threadNum = 4
    # Lista para guardar as instâncias das classes criadas
    threadsList = []
    
    # Crias uma instância da classe [i]NomeAqui[/i] para cada thread
    for i in range( 0, threadNum ):
        # Cria uma nova instância
        newThread = NomeAqui()
        # Adiciona a nova instância à lista
        threadsList.append( newThread )


    # Depois de tudo criado está na altura de iniciar as threads criadas
    for thread in threadsList:
        # Este metodo "start" invoca o metodo "run" que foi criado na classe "NomeAqui"
        threads.start()

Tenta agora adaptar o teu código a esta estrutura e vai colocando as dúvidas.

Cumps!

Link to comment
Share on other sites

Li o teu primeiro post com mais atenção e reparei na parte em que dizes que uma das execuções demora cerca de 8h... É muito processamento... O que te indiquei serve para coisas simples sem grande peso a nível de processamento. Devido a uma caracteristica de Python a eficiencia das threads deixa bastante a desejar e uma forma de contornar essa limitação seria realmente utilizar o modulo multiprocessing. Mas para ser sincero, com essa quantidade de informação para processar devias pensar em utilizar outra linguagem(C/C++) que não python e ias vez essas 8h reduzirem bastante...

Para teres uma ideia da performance que podes ganhar fazendo isso em C/C++, à uns dias estive a fazer uns testes em que analisava pixel a pixel uma imagem de aproximandamente de 50x60. Fiz em python e em C, e entre outras coisas, o tempos de execução foram alguo deste tipo:

- python: 4 segundos

- C: < 1 segundo

Bem, isto não é assim tão linear e varia muito com o que estás a trabalhar... Mas é certo que vais reduzir bem esse tempo se fizeres isso em C/C++...

Cumps!

Link to comment
Share on other sites

pois, eu por mim fazia isto em c/c++ mas como a cadeira exige que o trabalho seja feito em python lá tem de ser...

é assim, a segunda função, recorrendo a um único for para percorre todos os anos(10), categorias de professores e escolas tem de fazer aproximadamente, 1494500000 iterações para cada ano( ~140 categorias, ~305 escolas, ~35000 professores num ano). a terceira função ainda vai adicionar mais um parâmetro a verificar( o grau do professor, licenciado, mestre ou doutor) e essa ainda vai demorar mais tempo. é por esta razão que queria dividir o for, fazendo assim com que conseguisse cortar o tempo pois estava a processar vários anos ao mesmo tempo.

o que estava a tentar fazer também não é propriamente pedido, era apenas um 'extra' que queria adicionar pois permitia-me fazer todas as combinações de estatísticas para depois meter numa página html.

vou antes fazer uma variação em que fixo um ou mais parâmetros, reduzindo assim o número de procuras...

é que se tiver os parâmetros fixos, tipo o número de assistentes no ist no ano 2000 , a função é extremamente rápida.

Link to comment
Share on other sites

Não sei como é o estado da arte, mas em 2009 o Python não tinha threads reais, ou melhor, as threads que existiam no Python, não eram threads do kernel.

O Python tem algo chamado Global Interpreter Lock (GIL), que na verdade impede que o Python aproveite realmente o hardware (por exemplo aproveitar multiplos cores do processador). O GIL é uma decisão de design do Guido e o Guido, não é conhecido como sendo uma pessoa que faz aquilo que acha ser errado (mesmo que haja quem ache que ele não tem razão e que lhe diga muitas vezes), como tal presumo que não hajam grandes mudanças... Ainda para mais ele já tentou remover o GIL uma vez e achou que resultou mal. O djthyrax, já mencionou um outro tópico aqui do fórum onde se diz isto (mas eu já tinha escrito e preferi reforçar).

Se o motivo que tens para paralelizar com threads é aproveitar o hardware, então sugiro que não uses threads (ou não uses Python).

Implementar threads é um assunto complicado e o Guido resolveu fazer este compromisso para garantir mais segurança. Outras linguagens como o Perl têm soluções diferentes (o Perl consegue aproveitar os multiplos processadores/cores), mas têm outras limitações. Outro problema em linguagens como o Perl e como o Python, é a diferença entre sistemas operativos (principalmente entre o window$ e os *nix).

Link to comment
Share on other sites

Não sei como é o estado da arte, mas em 2009 o Python não tinha threads reais, ou melhor, as threads que existiam no Python, não eram threads do kernel.

Pelo menos em windows e em Linux actualmente Python cria threads reais. O problema é mesmo o GIL... E é um problema tal que em determinadas situações um processamento em serie é mais rápido que paralelizando com threads...

Cumps!

Link to comment
Share on other sites

Usa processos... Abre um socket (ou mais), usa ficheiros, ou algo do género e por exemplo serialização para partilhares os dados por esse meio de comunicação...

Atenção que eu nem li o teu código, estou só a fazer sugestões genéricas... Alternativas à utilização de threads e variáveis partilhadas entre as threads...

Link to comment
Share on other sites

Sob o risco de soar um pouco arrogante, para o teu caso esta discussão toda sobre threads e processos no contexto de processadores multi-core é basicamente inútil.

O que te está a empatar é entrada e saída de dados (IO), não é processamento. O teu processador tem tempo de ir ao café beber uma cerveja e voltar enquanto ligas abres e fechas ligações à tua base de dados.

1. Abre a ligação à base de dados uma vez e desliga-a só no fim. Não faz sentido fazer o que estar sempre a abrir e fechar e eu diria que é o que te está a demorar mais tempo.

2. Paraleliza mais, como te der mais jeito e de acordo com as características dos sistemas onde isto corre. Eu neste caso começaria logo por usar o modulo threading do python por ser o mais simples. E aumentaria o número de threads mesmo para 10 ou 20.

Se tiveres memória com fartura usa o máximo que puderes, se o modulo de threading não te permitir fazer isto tanto quanto desejes, experimenta processos, ou as duas coisas.

Eu não sei até que ponto é que tens liberdade para mudar isto, provavelmente o teu professor fez-te uma carrada de exigências. Mas ainda estou para perceber a utilidade (o sentido?) de uma aplicação que carrega  dados 'on demand' de uma base de dados relacional convencinal por via de SQL e ao mesmo tempo tira partido de optimização de arquitecturas multi-core actuais. É como tentar quitar uma bicicleta com um motor de um ferrari.

Link to comment
Share on other sites

não é pelo facto de fazer a ligação à bd uma vez por ano que o programa se torna lento. para os 10 anos estamos a falar de ~3s no tempo de execução do programa. o problema está mesmo na quantidade de vezes que tenho de percorrer a lista resultante do pedido à bd. para a situação apontada o programa corre em pouco mais de 3s. o problema está quando tem de fazer algo assim :

def getTeacherNumberCategoryEstablismentYear(self, year, schoolName, profCategory, databaseContent):
        profs = {}        

        position = 0
        for line in databaseContent:
            if(schoolName == line[8].encode('utf-8')):
                if(profCategory == line[4].encode('utf-8')):
                    position = len(profs)
                    profs[position] = line[1].encode('utf-8')
                    pass
                pass
            pass
        pass

        return profs

ou assim:

def getTeacherNumberDegreeTypeEstablismentYearCategory(self, year, schoolName, degreeType, profCategory, databaseContent):
        
        profs = {}
        
        position = 0
        for line in databaseContent:
            if(schoolName == line[8].encode('utf-8')):
                if(degreeType == line[2].encode('utf-8')[0]):
                    if(profCategory == line[4].encode('utf-8')):
                        position = len(profs)
                        profs[position] = line[1].encode('utf-8'))
                        pass
                    pass
                pass
            pass
        pass

        return profs

deixo aqui a forma como estou a fazer a primeira função para todos os anos:


for year in range (2000, 2011):

    connection = sqlite3.connect('RebidesDBMulti.db')
    c = connection.cursor()
    c.execute(''' SELECT * FROM prof_data_year_{0} '''.format(year))
    databaseContent = c.fetchall()
    c.close()

    escolas = obj.getSchoolNamePerYear(year, databaseContent)
    categorias = obj.getCategoryNamePerYear(year, databaseContent)

    for categoria in categorias:
        
        for escola in escolas:

            dicProfs = obj.getTeacherNumberCategoryEstablismentYear(year, escola, categoria, databaseContent)
            dicCat[categoria] = dicProfs
            pass
        dicEscola[escola] = dicCat
    pass

    dicAno[year] = dicEscola

pass

ps: agora estava aqui a ver e sou capaz de ganhar algum tempo se for procurar apenas pelas categorias existentes numa certa escola em vez de ir procurar por todas as categorias existentes num ano...

algo tipo isto:


for year in range (2000, 2011):

    connection = sqlite3.connect('RebidesDBMulti.db')
    c = connection.cursor()
    c.execute(''' SELECT * FROM prof_data_year_{0} '''.format(year))
    databaseContent = c.fetchall()
    c.close()

    escolas = obj.getSchoolNamePerYear(year, databaseContent)

    for escola in escolas:

        categorias = {}
        for line in databaseContent:
             if(escola== line[8].encode('utf-8')):
                    posicao = len(categorias)
                    categorias[posicao] = line[4].encode('utf-8')
        
        for categoria in categorias:

            dicProfs = obj.getTeacherNumberCategoryEstablismentYear(year, escola, categoria, databaseContent)
            dicCat[categoria] = dicProfs
            pass
        dicEscola[escola] = dicCat
    pass

    dicAno[year] = dicEscola

pass

Link to comment
Share on other sites

bom, parece-me que a alteração que fiz meteu-me o ciclo para um ano a demorar 77segundos...

se for assim, estamos a falar de 770 para os 10 anos.

edit: depois de 880 segundos tenho os resultados. aquela pequena alteração conseguiu poupar paletes de tempo...

edit2: voltei a alterar o método e em vez de percorrer a lista com base de dados referente a um ano, passei a percorrer uma lista com apenas os dados referentes à escola em questão no ciclo e consegui passar dos 880 segundos para os 113.71.

Link to comment
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now
 Share

×
×
  • Create New...

Important Information

By using this site you accept our Terms of Use and Privacy Policy. We have placed cookies on your device to help make this website better. You can adjust your cookie settings, otherwise we'll assume you're okay to continue.