Rumah >pembangunan bahagian belakang >Tutorial Python >Mengapa API Berbilang Thread Saya Masih Lambat?

Mengapa API Berbilang Thread Saya Masih Lambat?

DDD
DDDasal
2024-12-07 20:49:17793semak imbas

Why is My Multi-Threaded API Still Slow?

Saya menghadapi masalah dengan API saya dan saya berharap seseorang boleh membantu. Walaupun menambah berbilang benang, peningkatan prestasi adalah jauh daripada apa yang saya jangkakan. Sebaik-baiknya, jika satu utas mengambil masa 1 saat untuk menyelesaikan tugas, maka 10 utas berjalan serentak juga perlu mengambil masa kira-kira 1 saat (itu pemahaman saya). Walau bagaimanapun, masa respons API saya masih sangat perlahan.

Masalahnya

Saya menggunakan FastAPI bersama-sama dengan perpustakaan seperti Playwright, MongoDB dan ThreadPoolExecutor. Matlamatnya adalah untuk menggunakan threading untuk tugas terikat CPU dan async-menunggu untuk tugas terikat IO. Namun, masa respons saya tidak bertambah baik seperti yang dijangkakan.

Contoh Automasi Buku

Salah satu bahagian projek saya melibatkan mengautomasikan pertanyaan buku menggunakan Playwright untuk berinteraksi dengan penonton EPUB. Fungsi berikut menggunakan Playwright untuk membuka penyemak imbas, menavigasi ke halaman buku dan melakukan carian:

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

Fungsi di atas bertujuan untuk tidak segerak untuk mengelak daripada menyekat tugasan lain. Walau bagaimanapun, walaupun dengan persediaan async ini, prestasi masih tidak seperti yang diharapkan.
Nota: Saya telah mengira masa yang diambil untuk membuka buku dan menjalankan pertanyaan satu buku adalah lebih kurang 0.0028s

Contoh Refactor

Saya menggunakan run_in_executor() untuk melaksanakan fungsi dalam ProcessPoolExecutor, cuba mengelakkan GIL dan mengurus beban kerja dengan betul.

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

soalan

Walaupun selepas perubahan ini, API saya masih perlahan. Apa yang saya hilang? Adakah sesiapa yang menghadapi masalah serupa dengan persediaan GIL, threading, atau async Python? Sebarang nasihat akan sangat dihargai!

Atas ialah kandungan terperinci Mengapa API Berbilang Thread Saya Masih Lambat?. Untuk maklumat lanjut, sila ikut artikel berkaitan lain di laman web China PHP!

Kenyataan:
Kandungan artikel ini disumbangkan secara sukarela oleh netizen, dan hak cipta adalah milik pengarang asal. Laman web ini tidak memikul tanggungjawab undang-undang yang sepadan. Jika anda menemui sebarang kandungan yang disyaki plagiarisme atau pelanggaran, sila hubungi admin@php.cn