Что это за cprofile результат, говорящий мне, что мне нужно исправить?

StackOverflow https://stackoverflow.com/questions/3898266

Вопрос

Я хотел бы улучшить производительность сценария Python и использовать cProfile Чтобы генерировать отчет о производительности:

python -m cProfile -o chrX.prof ./bgchr.py ...args...

Я открыл это chrX.prof Файл с помощью Python pstats и напечатал статистику:

Python 2.7 (r27:82500, Oct  5 2010, 00:24:22) 
[GCC 4.1.2 20080704 (Red Hat 4.1.2-44)] on linux2
Type "help", "copyright", "credits" or "license" for more information.
>>> import pstats
>>> p = pstats.Stats('chrX.prof')
>>> p.sort_stats('name')
>>> p.print_stats()                                                                                                                                                                                                                        
Sun Oct 10 00:37:30 2010    chrX.prof                                                                                                                                                                                                      

         8760583 function calls in 13.780 CPU seconds                                                                                                                                                                                      

   Ordered by: function name                                                                                                                                                                                                               

   ncalls  tottime  percall  cumtime  percall filename:lineno(function)                                                                                                                                                                    
        1    0.000    0.000    0.000    0.000 {_locale.setlocale}                                                                                                                                                                          
        1    1.128    1.128    1.128    1.128 {bz2.decompress}                                                                                                                                                                             
        1    0.002    0.002   13.780   13.780 {execfile}                                                                                                                                                                                   
  1750678    0.300    0.000    0.300    0.000 {len}                                                                                                                                                                                        
       48    0.000    0.000    0.000    0.000 {method 'append' of 'list' objects}                                                                                                                                                          
        1    0.000    0.000    0.000    0.000 {method 'close' of 'file' objects}                                                                                                                                                           
        1    0.000    0.000    0.000    0.000 {method 'disable' of '_lsprof.Profiler' objects}                                                                                                                                             
  1750676    0.496    0.000    0.496    0.000 {method 'join' of 'str' objects}                                                                                                                                                             
        1    0.007    0.007    0.007    0.007 {method 'read' of 'file' objects}                                                                                                                                                            
        1    0.000    0.000    0.000    0.000 {method 'readlines' of 'file' objects}                                                                                                                                                       
        1    0.034    0.034    0.034    0.034 {method 'rstrip' of 'str' objects}                                                                                                                                                           
       23    0.000    0.000    0.000    0.000 {method 'seek' of 'file' objects}                                                                                                                                                            
  1757785    1.230    0.000    1.230    0.000 {method 'split' of 'str' objects}                                                                                                                                                            
        1    0.000    0.000    0.000    0.000 {method 'startswith' of 'str' objects}                                                                                                                                                       
  1750676    0.872    0.000    0.872    0.000 {method 'write' of 'file' objects}                                                                                                                                                           
        1    0.007    0.007   13.778   13.778 ./bgchr:3(<module>)                                                                                                                                                                          
        1    0.000    0.000   13.780   13.780 <string>:1(<module>)                                                                                                                                                                         
        1    0.001    0.001    0.001    0.001 {open}                                                                                                                                                                                       
        1    0.000    0.000    0.000    0.000 {sys.exit}                                                                                                                                                                                   
        1    0.000    0.000    0.000    0.000 ./bgchr:36(checkCommandLineInputs)                                                                                                                                                           
        1    0.000    0.000    0.000    0.000 ./bgchr:27(checkInstallation)                                                                                                                                                                
        1    1.131    1.131   13.701   13.701 ./bgchr:97(extractData)                                                                                                                                                                      
        1    0.003    0.003    0.007    0.007 ./bgchr:55(extractMetadata)                                                                                                                                                                  
        1    0.064    0.064   13.771   13.771 ./bgchr:5(main)                                                                                                                                                                              
  1750677    8.504    0.000   11.196    0.000 ./bgchr:122(parseJarchLine)                                                                                                                                                                  
        1    0.000    0.000    0.000    0.000 ./bgchr:72(parseMetadata)                                                                                                                                                                    
        1    0.000    0.000    0.000    0.000 /home/areynolds/proj/tools/lib/python2.7/locale.py:517(setlocale) 

Вопрос: Что я могу сделать join, split и write Операции по снижению очевидного воздействия они имеют на выполнении этого сценария?

Если это актуально, вот полный исходный код к сценарию под вопросом:

#!/usr/bin/env python

import sys, os, time, bz2, locale

def main(*args):
    # Constants
    global metadataRequiredFileSize
    metadataRequiredFileSize = 8192
    requiredVersion = (2,5)

    # Prep
    global whichChromosome
    whichChromosome = "all"
    checkInstallation(requiredVersion)
    checkCommandLineInputs()
    extractMetadata()
    parseMetadata()
    if whichChromosome == "--list":
        listMetadata()
        sys.exit(0)

    # Extract
    extractData()   
    return 0

def checkInstallation(rv):
    currentVersion = sys.version_info
    if currentVersion[0] == rv[0] and currentVersion[1] >= rv[1]:
        pass
    else:
        sys.stderr.write( "\n\t[%s] - Error: Your Python interpreter must be %d.%d or greater (within major version %d)\n" % (sys.argv[0], rv[0], rv[1], rv[0]) )
        sys.exit(-1)
    return

def checkCommandLineInputs():
    cmdName = sys.argv[0]
    argvLength = len(sys.argv[1:])
    if (argvLength == 0) or (argvLength > 2):
        sys.stderr.write( "\n\t[%s] - Usage: %s [<chromosome> | --list] <bjarch-file>\n\n" % (cmdName, cmdName) )
        sys.exit(-1)
    else:   
        global inFile
        global whichChromosome
        if argvLength == 1:
            inFile = sys.argv[1]
        elif argvLength == 2:
            whichChromosome = sys.argv[1]
            inFile = sys.argv[2]
        if inFile == "-" or inFile == "--list":
            sys.stderr.write( "\n\t[%s] - Usage: %s [<chromosome> | --list] <bjarch-file>\n\n" % (cmdName, cmdName) )
            sys.exit(-1)
    return

def extractMetadata():
    global metadataList
    global dataHandle
    metadataList = []
    dataHandle = open(inFile, 'rb')
    try:
        for data in dataHandle.readlines(metadataRequiredFileSize):     
            metadataLine = data
            metadataLines = metadataLine.split('\n')
            for line in metadataLines:      
                if line:
                    metadataList.append(line)
    except IOError:
        sys.stderr.write( "\n\t[%s] - Error: Could not extract metadata from %s\n\n" % (sys.argv[0], inFile) )
        sys.exit(-1)
    return

def parseMetadata():
    global metadataList
    global metadata
    metadata = []
    if not metadataList: # equivalent to "if len(metadataList) > 0"
        sys.stderr.write( "\n\t[%s] - Error: No metadata in %s\n\n" % (sys.argv[0], inFile) )
        sys.exit(-1)
    for entryText in metadataList:
        if entryText: # equivalent to "if len(entryText) > 0"
            entry = entryText.split('\t')
            filename = entry[0]
            chromosome = entry[0].split('.')[0]
            size = entry[1]
            entryDict = { 'chromosome':chromosome, 'filename':filename, 'size':size }
            metadata.append(entryDict)
    return

def listMetadata():
    for index in metadata:
        chromosome = index['chromosome']
        filename = index['filename']
        size = long(index['size'])
        sys.stdout.write( "%s\t%s\t%ld" % (chromosome, filename, size) )
    return

def extractData():
    global dataHandle
    global pLength
    global lastEnd
    locale.setlocale(locale.LC_ALL, 'POSIX')
    dataHandle.seek(metadataRequiredFileSize, 0) # move cursor past metadata
    for index in metadata:
        chromosome = index['chromosome']
        size = long(index['size'])
        pLength = 0L
        lastEnd = ""
        if whichChromosome == "all" or whichChromosome == index['chromosome']:
            dataStream = dataHandle.read(size)
            uncompressedData = bz2.decompress(dataStream)
            lines = uncompressedData.rstrip().split('\n')
            for line in lines:
                parseJarchLine(chromosome, line)
            if whichChromosome == chromosome:
                break
        else:
            dataHandle.seek(size, 1) # move cursor past chromosome chunk

    dataHandle.close()
    return

def parseJarchLine(chromosome, line):
    global pLength
    global lastEnd
    elements = line.split('\t')
    if len(elements) > 1:
        if lastEnd:
            start = long(lastEnd) + long(elements[0])
            lastEnd = long(start + pLength)
            sys.stdout.write("%s\t%ld\t%ld\t%s\n" % (chromosome, start, lastEnd, '\t'.join(elements[1:])))
        else:
            lastEnd = long(elements[0]) + long(pLength)
            sys.stdout.write("%s\t%ld\t%ld\t%s\n" % (chromosome, long(elements[0]), lastEnd, '\t'.join(elements[1:])))
    else:
        if elements[0].startswith('p'):
            pLength = long(elements[0][1:])
        else:
            start = long(long(lastEnd) + long(elements[0]))
            lastEnd = long(start + pLength)
            sys.stdout.write("%s\t%ld\t%ld\n" % (chromosome, start, lastEnd))               
    return

if __name__ == '__main__':
    sys.exit(main(*sys.argv))

РЕДАКТИРОВАТЬ

Если я прокомментирую sys.stdout.write заявление в первом условном parseJarchLine(), Тогда мое время выполнения идет от 10,2 с до 4,8 секунды:

# with first conditional's "sys.stdout.write" enabled
$ time ./bgchr chrX test.bjarch > /dev/null
real    0m10.186s                                                                                                                                                                                        
user    0m9.917s                                                                                                                                                                                         
sys 0m0.160s  

# after first conditional's "sys.stdout.write" is commented out                                                                                                                                                                                           
$ time ./bgchr chrX test.bjarch > /dev/null
real    0m4.808s                                                                                                                                                                                         
user    0m4.561s                                                                                                                                                                                         
sys 0m0.156s

Пишет stdout Действительно так дорого в Python?

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

Решение

ncalls актуальны только в той мере, в которой сравнение чисел в отношении других количеств, таких как количество символов / полей / строк в файле, могут высокими аномалиями; tottime и cumtime это то, что действительно имеет значение. cumtime это время, проведенное в функции/методе в том числе время, проведенное в функциях / методах, которые он называет; tottime это время, проведенное в функции/методе без учета время, проведенное в функциях/методах, которые он вызывает.

Я нахожу полезным сортировать статистику на tottime и снова дальше cumtime, не на name.

bgchar определенно относится к выполнению сценария и не является неактуальным, поскольку он занимает 8,9 секунды из 13,5; Это 8,9 секунды не включают в себя время в функции / методы, которые он называет! Читайте внимательно, что @Lie Ryan говорит о модульском вашим сценарии в функции и реализовать его советы. Точно так же, что говорит @josesy.

string упоминается, потому что вы import string и использовать его только в одном месте: string.find(elements[0], 'p'). Анкет На другой строке на выходе вы заметите, что String.find назывался только один раз, поэтому это не проблема с производительностью в этом прогоне этого сценария. Однако: вы используете str Методы везде. string функции устарели в настоящее время и реализуются путем вызова соответствующего str метод Тебе лучше писать elements[0].find('p') == 0 Для точного, но более быстрых эквивалентных и может использовать elements[0].startswith('p') что спасет читателей, удивляющихся ли это == 0 на самом деле должен быть == -1.

Четыре метода, упомянутые @bernd Petersohn, занимают всего 3,7 секунды из общего времени исполнения 13,541 секунды. Прежде чем слишком беспокоиться об этом, модульна свой скрипт в функции, снова запустите CProfile и сортируйте статистику по tottime.

Обновление за вопросом пересмотрено с помощью измененного сценария:

«« Вопрос: Что я могу сделать присоединиться, разделеть и писать операции, чтобы уменьшить очевидное воздействие, которое они имеют на выполнении этого сценария? »

Хм? Эти 3 вместе берут 2,6 секунды из общего числа 13,8. Ваша функция Parsejarchline занимает 8,5 секунды (что не включает время, выполненное функциями/методами, которые она вызывает. assert(8.5 > 2.6)

Бернд уже указал на то, что вы можете подумать с тем. Вы абсолютно разделяете линию, только чтобы снова присоединиться к нему при написании этого. Вам нужно проверить только первый элемент. Вместо elements = line.split('\t') делать elements = line.split('\t', 1) и заменить '\t'.join(elements[1:]) по elements[1].

Теперь давайте погрузимся в тело парсеярхлина. Количество используемых в источнике и способе использования long Встроенная функция удивительна. Также удивительный это тот факт, что long не упоминается на выходе Cprofile.

Почему вам нужно long вообще? Файлы более 2 ГБ? Хорошо, тогда вам нужно рассмотреть это с момента Python 2.2, int переполнение вызывает повышение long вместо того, чтобы повысить исключение. Вы можете воспользоваться быстрее выполнения int арифметика. Вы также должны учитывать, что делать long(x) когда x уже явно long пустая трата ресурсов.

Вот функция Parsejarchline с отмеченными изменениями отходов от отходов [1] и отмеченными изменениями на размышления [2]. Хорошая идея: внесите изменения в небольшие шаги, перепроцестируйте, перепрофилировали.

def parseJarchLine(chromosome, line):
    global pLength
    global lastEnd
    elements = line.split('\t')
    if len(elements) > 1:
        if lastEnd != "":
            start = long(lastEnd) + long(elements[0])
            # [1] start = lastEnd + long(elements[0])
            # [2] start = lastEnd + int(elements[0])
            lastEnd = long(start + pLength)
            # [1] lastEnd = start + pLength
            sys.stdout.write("%s\t%ld\t%ld\t%s\n" % (chromosome, start, lastEnd, '\t'.join(elements[1:])))
        else:
            lastEnd = long(elements[0]) + long(pLength)
            # [1] lastEnd = long(elements[0]) + pLength
            # [2] lastEnd = int(elements[0]) + pLength
            sys.stdout.write("%s\t%ld\t%ld\t%s\n" % (chromosome, long(elements[0]), lastEnd, '\t'.join(elements[1:])))
    else:
        if elements[0].startswith('p'):
            pLength = long(elements[0][1:])
            # [2] pLength = int(elements[0][1:])
        else:
            start = long(long(lastEnd) + long(elements[0]))
            # [1] start = lastEnd + long(elements[0])
            # [2] start = lastEnd + int(elements[0])
            lastEnd = long(start + pLength)
            # [1] lastEnd = start + pLength
            sys.stdout.write("%s\t%ld\t%ld\n" % (chromosome, start, lastEnd))               
    return

Обновление за вопросом о sys.stdout.write

Если заявление, которое вы прокомментировали, было чем -то вроде оригинального:

sys.stdout.write("%s\t%ld\t%ld\t%s\n" % (chromosome, start, lastEnd, '\t'.join(elements[1:])))

Тогда ваш вопрос ... интересный. Попробуй это:

payload = "%s\t%ld\t%ld\t%s\n" % (chromosome, start, lastEnd, '\t'.join(elements[1:]))
sys.stdout.write(payload)

Теперь прокомментировать sys.stdout.write утверждение ...

Кстати, кто -то упомянул в комментарии о том, как разбить это на несколько напитков ... вы рассматривали это? Сколько байтов в среднем в элементах [1:]? В хромосоме?

=== Изменение темы: меня беспокоит, что вы инициализируете lastEnd к "" а не до нуля, и что никто не прокомментировал это. Любой способ, вы должны исправить это, что позволяет довольно резки упростить плюс добавление в другие предложения:

def parseJarchLine(chromosome, line):
    global pLength
    global lastEnd
    elements = line.split('\t', 1)
    if elements[0][0] == 'p':
        pLength = int(elements[0][1:])
        return
    start = lastEnd + int(elements[0])
    lastEnd = start + pLength
    sys.stdout.write("%s\t%ld\t%ld" % (chromosome, start, lastEnd))
    if elements[1:]:
        sys.stdout.write(elements[1])
    sys.stdout.write(\n)

Теперь я похож на две глобальные переменные lastEnd и pLength - Функция Parsejarchline теперь настолько мала, что она может быть сложена обратно в корпус своего единственного звонящего, extractData, который экономит две глобальные переменные, а также вызовы функций Gazillion. Вы также можете сохранить поиск газиллиона sys.stdout.write Поместив write = sys.stdout.write Когда -то вверх extractData и используя это вместо этого.

BTW, скрипт тесты для Python 2.5 или лучше; Вы пробовали профилирование на 2,5 и 2.6?

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

Этот выход будет более полезен, если ваш код будет более модульным, поскольку указывалось, как лежат Райан. Однако пара вещей вы можете забрать с вывода и просто глядя на исходный код:

Вы делаете много сравнений, которые на самом деле не нужны в Python. Например, вместо:

if len(entryText) > 0:

Вы можете просто написать:

if entryText:

Пустой список оценивает ложь в Python. То же самое верно для пустой строки, которую вы также проверяете в своем коде, и изменяя его также сделать код немного более коротким и читаемым, поэтому вместо этого:

   for line in metadataLines:      
        if line == '':
            break
        else:
            metadataList.append(line)

Вы можете просто сделать:

for line in metadataLines:
    if line:
       metadataList.append(line)

Есть несколько других вопросов с этим Кодексом с точки зрения обеих организаций, так и на производительности. Вы назначаете переменные несколько раз к тому же, а не просто созданию экземпляра объекта один раз и выполняя все доступ к объекту, например. Это уменьшит количество заданий, а также количество глобальных переменных. Я не хочу звучать чрезмерно критично, но этот код не будет написан с учетом производительности.

Записи, относящиеся к возможной оптимизации, являются теми, которые имеют высокие значения для ncalls и TOTTIME. bgchr:4(<module>) и <string>:1(<module>) Вероятно, обратитесь к выполнению корпуса вашего модуля и здесь не актуальны.

Очевидно, что ваша проблема с производительностью происходит от обработки строк. Возможно, это должно быть уменьшено. Горячие точки split, join и sys.stdout.write. bz2.decompress также кажется дорогостоящим.

Я предлагаю вам попробовать следующее:

  • Ваши основные данные, кажется, состоят из вкладки разделенных значений CSV. Попробуйте, если читатель CSV работает лучше.
  • Sys.Stdout - это забавочная и промывается каждый раз, когда написана новая линия. Подумайте о том, чтобы написать в файл с большим размером буфера.
  • Вместо того, чтобы соединять элементы, прежде чем написать их, напишите их последовательно в выходной файл. Вы также можете рассмотреть возможность использования CSV Writer.
  • Вместо одновременно распаковывать данные в одну строку, используйте объект BZ2File и пропустите, чтобы к читателю CSV.

Кажется, что корпус петли, который на самом деле не раскалывает данные, вызывается только один раз. Возможно, вы найдете способ избежать звонка dataHandle.read(size), который создает огромную строку, которая затем распадается, и напрямую работать с объектом файла.

Дополнение: Bz2file, вероятно, не применим в вашем случае, потому что он требует аргумента имени файла. Что вам нужно, так это что -то вроде представления файла с интегрированным пределом чтения, сравнимого с ZipextFile, но с использованием BZ2Decompressor для декомпрессии.

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

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