Becnhmark-Test für Arme (SQLite-Geschwindigkeit)
execute() übergibst du SQL oder was auch immer SQLAlchemy zu SQL konvertieren kann und Tupel mit den Werten die eingesetzt werden sollen. In diesem Fall heisst dass das wir die Namen die wir von generate_names() bekommen in ein Tupel stecken müssen und diese Tupel übergeben wir dann als Argumente an execute(). Das ist was hier passiert. Die GC erzeugt die Tupel und mit dem Sternchen packen wir die GC aus damit jedes Tupel als einzelnes Argument übergeben wird.
@DasIch, @Sophus: das * sorgt dafür, dass die durch die GC erzeugten Elemente als Parameter an execute übergeben werden. Die liegen also gleichzeitig im Speicher. Daher ist es ja auch besser, nicht irgendwelche undokumentierten Funktionalitäten zu nehmen, sondern gleich die dafür vorgesehene Funktion executemany:
Code: Alles auswählen
connection.executemany(
'INSERT INTO test_table (generated_name) VALUES (?)',
((name, ) for name in islice(generate_names(), number_of_records))
)
Connection hat keine executemany Methode. Diese Funktionalität ist dokumentiert.
Der ”Fehler” hier ist SQLAlchemy mit so simplem SQL als Zeichenkette zu verwenden. Das verwirrt offenbar sehr viele Leute, weil man das eigentlich nicht macht. Ich bin am Anfang ja auch darauf reingefallen.
Code: Alles auswählen
#!/usr/bin/env python3.5
import os
from time import perf_counter
from functools import partial
from itertools import count, islice
from sqlalchemy import create_engine, MetaData, Table, Column, Integer, String
DATABASE_PATH = 'test.sqlite'
DATABASE_URI = 'sqlite:///' + DATABASE_PATH
metadata = MetaData()
test_table = Table('test_table', metadata,
Column('id', Integer, primary_key=True),
Column('generated_name', String(100), unique=True)
)
def remove_file(filepath):
try:
os.remove(filepath)
except FileNotFoundError:
pass
except:
raise
def input(prompt, type=None):
while True:
raw_answer = __builtins__.input(prompt)
if type is None:
return raw_answer
try:
return type(raw_answer)
except ValueError:
print('{!r} is not a {}'.format(raw_answer, type))
def benchmark(function):
start = perf_counter()
function()
end = perf_counter()
return end - start
def generate_names():
for i in count():
yield 'Name #{}'.format(i)
def insert_records(connection, number_of_records):
values = (
{'generated_name': name}
for name in islice(generate_names(), number_of_records)
)
connection.execute(test_table.insert(), *values)
def main():
remove_file(DATABASE_PATH)
engine = create_engine(DATABASE_URI)
with engine.connect() as connection:
metadata.create_all(connection)
number_of_records = input('How many records should be inserted? ', type=int)
duration = benchmark(partial(insert_records, connection, number_of_records))
print('Duration: {}s'.format(duration))
if __name__ == '__main__':
main()
@DasIch: ich war verwirrt, weil Du statt der austauschbaren DB-API2 die nicht-konformen Erweiterungen von sqlite3 benutzt. Das macht das wechseln der Datenbank unnötig kompliziert. Arbeite mit Cursor-Objekten und executemany, das zusätzlich den Vorteil hat, dass der Generator auch wirklich benutzt wird.
@Sirius3: Du bist immer noch verwirrt, genau wie ich am Anfang: Es wird hier die ganze Zeit und von allen Beteiligten die Code zeigen SQLAlchemy verwendet!
@Sophus: Das ist nicht das Problem, sondern das niemand SQLAlchemy mit so simplem handgeschriebenen SQL in Zeichenketten verbindet. Denn dann macht SQLAlchemy keinen Sinn. (Du benutzt da übrigens mehr als nur den DDL-Teil von SQL.)
Und was die Geschwindigkeit vom ORM angeht: Die ist *ausreichend*. Oder meinst Du jemand kann schneller Filme in Deine Datenbank über die Qt-Oberfläche eintippen als das ORM sie in die Datenbank schreiben kann? Du machst Dir schon wieder mal Gedanken über Probleme die nicht existieren.
Und was die Geschwindigkeit vom ORM angeht: Die ist *ausreichend*. Oder meinst Du jemand kann schneller Filme in Deine Datenbank über die Qt-Oberfläche eintippen als das ORM sie in die Datenbank schreiben kann? Du machst Dir schon wieder mal Gedanken über Probleme die nicht existieren.
@BlackJack: Ich glaube, es muss ein Missverständnis sein. In meinem Projekt verwende ich selbstverständlich ORM - wobei ich diesen Hype um ORM nicht sonderlich gut finde. Schließlich ist es eher bereichernd zu wissen, wie eine DDL aussieht, um zu wissen, was die Datenbank da macht. Dies wird ja bei einem ORM "versteckt". Aber das ist ein anderes Thema. Ich wollte eher ein kleines Skript schreiben, um die Geschwindigkeit von SQLite zu testen. Und da hielt ich ORM unangebracht.