Heim > Backend-Entwicklung > Python-Tutorial > Warum ist meine Multithread-API immer noch langsam?

Warum ist meine Multithread-API immer noch langsam?

DDD
Freigeben: 2024-12-07 20:49:17
Original
778 Leute haben es durchsucht

Why is My Multi-Threaded API Still Slow?

Ich habe ein Problem mit meiner API und hoffe, dass mir jemand helfen kann. Trotz der Hinzufügung von Multithreading liegen die Leistungssteigerungen weit hinter meinen Erwartungen zurück. Wenn ein Thread 1 Sekunde benötigt, um eine Aufgabe abzuschließen, sollten im Idealfall 10 Threads, die gleichzeitig ausgeführt werden, ebenfalls etwa 1 Sekunde dauern (so mein Verständnis). Allerdings sind meine API-Antwortzeiten immer noch sehr langsam.

Das Problem

Ich verwende FastAPI zusammen mit Bibliotheken wie Playwright, MongoDB und ThreadPoolExecutor. Das Ziel bestand darin, Threading für CPU-gebundene Aufgaben und Async-Await für IO-gebundene Aufgaben zu verwenden. Dennoch verbessern sich meine Reaktionszeiten nicht wie erwartet.

Beispiel für Buchautomatisierung

Ein Teil meines Projekts besteht darin, Buchabfragen mithilfe von Playwright zu automatisieren, um mit einem EPUB-Viewer zu interagieren. Die folgende Funktion verwendet Playwright, um einen Browser zu öffnen, zur Seite eines Buches zu navigieren und Suchvorgänge durchzuführen:

from playwright.async_api import async_playwright
import asyncio

async def search_with_playwright(search_text: str, book_id: str):
    async with async_playwright() as p:
        browser = await p.chromium.launch(headless=True)
        page = await browser.new_page()
        book_id = book_id.replace("-1", "")
        book_url = f"http://localhost:8002/book/{book_id}"
        await page.goto(book_url)
        await page.fill("#searchInput", search_text)
        await page.click("#searchButton")
        await page.wait_for_selector("#searchResults")
        search_results = await page.evaluate('''
            () => {
                let results = [];
                document.querySelectorAll("#searchResults ul li").forEach(item => {
                    let excerptElement = item.querySelector("strong:nth-of-type(1)");
                    let cfiElement = item.querySelector("strong:nth-of-type(2)");

                    if (excerptElement && cfiElement) {
                        let excerpt = excerptElement.nextSibling ? excerptElement.nextSibling.nodeValue.trim() : "";
                        let cfi = cfiElement.nextSibling ? cfiElement.nextSibling.nodeValue.trim() : "";
                        results.push({ excerpt, cfi });
                    }
                });
                return results;
            }
        ''')
        await browser.close()
        return search_results
Nach dem Login kopieren

Die obige Funktion soll asynchron sein, um das Blockieren anderer Aufgaben zu vermeiden. Allerdings ist die Leistung selbst mit diesem asynchronen Setup immer noch nicht wie erwartet.
Hinweis: Ich habe berechnet, dass es etwa 0,0028 Sekunden dauert, ein Buch zu öffnen und ein einzelnes Buch abzufragen

Refactoring-Beispiel

Ich habe run_in_executor() verwendet, um Funktionen in ProcessPoolExecutor auszuführen und dabei versucht, die GIL zu vermeiden und Arbeitslasten richtig zu verwalten.

async def query_mongo(query: str, id: str):
    query_vector = generate_embedding(query)

    results = db[id].aggregate([
        {
            "$vectorSearch": {
                "queryVector": query_vector,
                "path": "embedding",
                "numCandidates": 2100,
                "limit": 50,
                "index": id
            }
        }
    ])

    # Helper function for processing each document
    def process_document(document):
        try:
            chunk = document["chunk"]
            chapter = document["chapter"]
            number = document["chapter_number"]
            book_id = id

            results = asyncio.run(search_with_playwright(chunk, book_id))
            return {
                "content": chunk,
                "chapter": chapter,
                "number": number,
                "results": results,
            }
        except Exception as e:
            print(f"Error processing document: {e}")
            return None

    # Using ThreadPoolExecutor for concurrency
    all_data = []
    with ThreadPoolExecutor() as executor:
        futures = {executor.submit(process_document, doc): doc for doc in results}

        for future in as_completed(futures):
            try:
                result = future.result()
                if result:  # Append result if it's not None
                    all_data.append(result)
            except Exception as e:
                print(f"Error in future processing: {e}")

    return all_data
Nach dem Login kopieren

Frage

Selbst nach diesen Änderungen ist meine API immer noch langsam. Was fehlt mir? Hat jemand ähnliche Probleme mit Pythons GIL-, Threading- oder asynchronen Setups gehabt? Für jeden Rat wäre ich sehr dankbar!

Das obige ist der detaillierte Inhalt vonWarum ist meine Multithread-API immer noch langsam?. Für weitere Informationen folgen Sie bitte anderen verwandten Artikeln auf der PHP chinesischen Website!

Quelle:dev.to
Erklärung dieser Website
Der Inhalt dieses Artikels wird freiwillig von Internetnutzern beigesteuert und das Urheberrecht liegt beim ursprünglichen Autor. Diese Website übernimmt keine entsprechende rechtliche Verantwortung. Wenn Sie Inhalte finden, bei denen der Verdacht eines Plagiats oder einer Rechtsverletzung besteht, wenden Sie sich bitte an admin@php.cn
Beliebte Tutorials
Mehr>
Neueste Downloads
Mehr>
Web-Effekte
Quellcode der Website
Website-Materialien
Frontend-Vorlage