لماذا يقرأ C++ الأسطر من stdin بشكل أبطأ من Python؟ تحليل وحل

مدرب بايثون أونلاين للمبتدئين

تعلم Python بسهولة دون إرهاق نظري. حل مهام عملية مع التحقق التلقائي واكتب الكود مباشرة في المتصفح.

ابدأ الدورة

مقدمة: مفارقة السرعة التي فجرت Stack Overflow

\n\n

تخيل الموقف: أنت تحل مسألة أولمبية أو تعالج ملف سجل ضخم بحجم جيجابايت. سكربت Python ينجز المهمة في ثانيتين. تعيد كتابته بلغة C++ متوقعاً تسريعاً عشرة أضعاف، لكن... تحصل على 10 ثوانٍ! ألم مألوف؟ هذا السؤال بالتحديد حصل على آلاف المشاهدات على Stack Overflow وأدى إلى نقاش أسطوري: «لماذا قراءة الأسطر من stdin أبطأ بكثير في C++ منها في Python؟».

\n\n

كيف يمكن لـ C++ «السريعة» أن تخسر أمام Python «البطيئة» في عملية بسيطة مثل قراءة الأسطر؟ الإجابة لا تكمن في سرعة اللغة نفسها، بل في الإعدادات الافتراضية وهندسة iostream. في هذه المقالة، سنجري تحقيقنا الخاص، ونكتب كود اختبار بلغة C++ وPython، ونقيس الوقت، والأهم من ذلك، سنتعلم كيفية جعل الإدخال والإخراج في C++ سريعاً حقاً.

\n\n

هذه المقالة ليست نظرية جافة، بل دليل عملي لكل من يكتب بلغة C++ ويواجه مشاكل أداء الإدخال والإخراج. هيا بنا!

\n\n

جوهر المشكلة: لماذا iostream «يتباطأ»؟

\n\n

عندما تكتب std::cin >> data أو std::getline(std::cin, line)، فإن C++ بشكل افتراضي يفعل شيئاً لا يعرفه الكثيرون. إنه يزامن تدفقات الإدخال والإخراج الخاصة به مع التدفقات القياسية للغة C (stdin, stdout). لماذا؟ من أجل التوافق: يمكنك استخدام printf و std::cout في نفس البرنامج في وقت واحد، ولن يختلط الإخراج.

\n\n

هذه المزامنة مكلفة. كل استدعاء لـ cin أو cout يتحقق من حالة تدفقات C، مما يحول العملية المخزنة مؤقتاً السريعة إلى عملية متزامنة بطيئة.

\n\n

في Python، الأمر مختلف تماماً. الدالة input() و sys.stdin.readline() تستخدمان تخزيناً مؤقتاً خاصاً بهما، محسّناً بشكل جيد على مستوى C. لا تزامن إضافي - مجرد قراءة كتلة من البيانات من واصف الملف. لهذا السبب، قراءة stdin في Python يمكن أن تكون أسرع من C++ «خارج الصندوق».

\n\n

تجربة: اختبار السرعة

\n\n

دعنا نتحقق من ذلك عملياً. سنكتب برنامجاً بسيطاً يقرأ 1,000,000 سطر من stdin ولا يفعل شيئاً بها (فقط يقرأها). أولاً بلغة C++ «كما هي»:

\n\n
// slow_cin.cpp\n#include <iostream>\n#include <string>\n\nint main() {\n    std::ios::sync_with_stdio(false); // سنعلقها حالياً\n    std::cin.tie(nullptr);           // وهذا أيضاً\n\n    std::string line;\n    while (std::getline(std::cin, line)) {\n        // لا نفعل شيئاً\n    }\n    return 0;\n}
\n\n

والآن بلغة Python:

\n\n
# fast_python.py\nimport sys\n\ndef main():\n    for line in sys.stdin:\n        pass\n\nif __name__ == "__main__":\n    main()
\n\n

سنشغل الاختبار باستخدام seq 1000000 | ./slow_cin و seq 1000000 | python3 fast_python.py. ستكون النتيجة صادمة: C++ بدون تحسينات يمكن أن يكون أبطأ بمقدار 5-10 مرات من Python! لكن لا تتعجل في دفن لغة ++C - فالمشكلة تُحل بسطرين من الكود.

\n\n

الحل: سطران سحريان

\n\n

كل ما هو مطلوب لتسريع الإدخال والإخراج في C++ هو تعطيل المزامنة وربط التدفقات. أضف في بداية main():

\n\n
std::ios::sync_with_stdio(false);\nstd::cin.tie(nullptr);
\n\n

السطر الأول يعطل المزامنة بين iostream و stdio. الثاني يفصل cin عن cout (افتراضياً، قبل كل عملية إدخال، يقوم cout بمسح المخزن المؤقت، مما يبطئ العمل أيضاً). بعد ذلك، سيبدأ كود C++ الخاص بك في العمل بمستوى Python، وغالباً أسرع.

\n\n

نتحقق من النتيجة

\n\n

الآن، قم بإلغاء تعليق هذين السطرين في اختبارنا وشغله مرة أخرى. سترى أن أداء C++ سيرتفع بشكل كبير. في بعض المقاييس، يصل الفرق إلى 10 أضعاف.

المدونات

توصيات الكتب