Huh? Why only half the time? We'll, every time we get a speedup, we spend half of it on making the system more robust. So for instance, here's how long it took to make 10,000 notes in 2008 and 2013:
2008 Sep ~1.1
2013 Nov 0.777
Well, that was a pretty good improvement. But there were all sorts of problems with tuplets in music21 (especially from MIDI), where, for instance, five quintuplet 16ths could add up to 0.9999 quarter notes. So we switched to a Fraction module for safety, and we lost the speedups:
2014 Jul 1.126
2015 Jan1 1.154
That seemed too slow, so in January, we undertook a large number of tweaks, described below, and got it down to:
2015 Jan19 0.516
We're still working, so in 2.0.10 when it is released you'll find that 10,000 notes now takes:
2015 Sep6 0.400
This gives a lot of room to play with to start making the system safer and more secure.
Deepcopy performance still leaves a lot to be desired. This will be the next focus.
This article will get updated as the timing improves (or is sacrificed for security).
>>> from timeit import timeit as t
========== Note
#1 Baseline
>>> t('n=note.Note()', 'from music21 import stream, note; import copy; n=note.Note()', number=10000)
1.154
>>> t('copy.deepcopy(n)', 'from music21 import stream, note; import copy; n=note.Note()', number=10000)
5.535
#2 Instantiation Tweaks
>>> t('n=note.Note()', 'from music21 import stream, note; import copy; n=note.Note()', number=10000)
0.690
>>> t('copy.deepcopy(n)', 'from music21 import stream, note; import copy; n=note.Note()', number=10000)
4.221
#3 Deepcopy of Durations
>>> t('n=note.Note()', 'from music21 import stream, note; import copy; n=note.Note()', number=10000)
0.698
>>> t('copy.deepcopy(n)', 'from music21 import stream, note; import copy; n=note.Note()', number=10000)
3.751
#4 Tweaks to Pitch
>>> t('n=note.Note()', 'from music21 import stream, note; import copy; n=note.Note()', number=10000)
0.662
>>> t('copy.deepcopy(n)', 'from music21 import stream, note; import copy; n=note.Note()', number=10000)
3.594
#5 Move imports out of frequently called objects
>>> t('n=note.Note()', 'from music21 import stream, note; import copy; n=note.Note()', number=10000)
0.516
#6 2.0.10 -- 2015 Sep improvements to seeing up durations and sites:
>>> t('n=note.Note()', 'from music21 import stream, note; import copy; n=note.Note()', number=10000)
0.400
>>> t('copy.deepcopy(n)', 'from music21 import stream, note; import copy; n=note.Note()', number=10000)
3.323
========= GeneralNote
# 1 Baseline
>>> t('n=note.GeneralNote()', 'from music21 import stream, note; import copy; n=note.Note()', number=10000)
0.754
>>> t('copy.deepcopy(n)', 'from music21 import stream, note; import copy; n=note.GeneralNote()', number=10000)
3.489
# 2 Instantiation Tweaks
>>> t('n=note.GeneralNote()', 'from music21 import stream, note; import copy; n=note.Note()', number=10000)
0.301
>>> t('copy.deepcopy(n)', 'from music21 import stream, note; import copy; n=note.GeneralNote()', number=10000)
1.888
# 3 Deepcopy of Durations
>>> t('n=note.GeneralNote()', 'from music21 import stream, note; import copy; n=note.Note()', number=10000)
0.311
>>> t('copy.deepcopy(n)', 'from music21 import stream, note; import copy; n=note.GeneralNote()', number=10000)
1.389
For comparison:
>>> t('n=note.NotRest()', 'from music21 import base, note; import copy;', number=10000)
0.361
>>> t('n=note.Rest()', 'from music21 import base, note; import copy;', number=10000)
0.299
>> t('n=note.Unpitched()', 'from music21 import base, note; import copy;', number=10000)
0.368
>>> t('copy.deepcopy(n)', 'from music21 import stream, note; import copy; n=note.Unpitched()', number=10000)
2.059
Chords are fast...
>>> t('c=chord.Chord()', 'from music21 import chord; import copy;', number=10000)
0.301
But each additional note is 0.5s per 10000
>>> t('c=chord.Chord(["C"])', 'from music21 import chord; import copy;', number=10000)
0.882
>>> t('c=chord.Chord(["C","E","G"])', 'from music21 import chord; import copy;', number=10000)
1.985
Pitches:
>>> t('copy.deepcopy(p)', 'from music21 import pitch; import copy; p=pitch.Pitch("C")', number=10000)
1.291
>>> t('p=pitch.Pitch("C")', 'from music21 import pitch; import copy; p=pitch.Pitch("C")', number=10000)
0.217
after tweaks:
>>> t('p=pitch.Pitch("C")', 'from music21 import pitch; import copy; p=pitch.Pitch("C")', number=10000)
0.189
Accidentals are .08s
>>> t('n=note.Note("C")', 'from music21 import stream, note; import copy; n=note.Note()', number=10000)
0.642
>>> t('n=note.Note("C#")', 'from music21 import stream, note; import copy; n=note.Note()', number=10000)
0.721
========= Music21Object
# 1 Baseline
>>> t('n=base.Music21Object()', 'from music21 import base, note; import copy; n=base.Music21Object()', number=10000)
0.113
>>> t('copy.deepcopy(n)', 'from music21 import base, note; import copy; n=base.Music21Object()', number=10000)
0.912
One subclass away (__init__ does nothing but call super(__init__):
>>> t('n=base.ElementWrapper()', 'from music21 import base, note; import copy;', number=10000)
0.308
>>> t('copy.deepcopy(n)', 'from music21 import base, note; import copy; n=base.ElementWrapper()', number=10000)
1.423
# 2 Sites and Duration improvements (Sep 2015)
>>> t('n=base.Music21Object()', 'from music21 import base, note; import copy; n=base.Music21Object()', number=10000)
0.034
Deepcopy is MUCH slower than just creating a new one...
>>> t('copy.deepcopy(n)', 'from music21 import base, note; import copy; n=base.Music21Object()', number=10000)
0.656
No comments:
Post a Comment