لا أعرف ما إذا كان هذا صحيحًا ، لكن عندما كنت أقرأ FAQ على أحد المشكلات التي توفر المواقع ، وجدت شيئًا يثير انتباهي:
تحقق من طرق الإدخال/الإخراج. في C++ ، استخدام cin و cout بطيء جدًا. استخدم هذه ، وسوف تضمن عدم القدرة على حل أي مشكلة مع كمية لا بأس بها من المدخلات أو المخرجات. استخدم printf و scanf بدلاً من ذلك.
هل يمكن لشخص توضيح ذلك؟ هل استخدام scanf () في برامج C++ أسرع من استخدام cin >> شيء ؟ إذا كانت الإجابة بنعم ، فهل هذه ممارسة جيدة لاستخدامها في برامج C++؟ اعتقدت أنه كان C محددة ، على الرغم من أنني أتعلم فقط C++ ...
إليك اختبار سريع لحالة بسيطة: برنامج لقراءة قائمة بالأرقام من الإدخال القياسي و XOR جميع الأرقام.
إصدار iostream:
#include <iostream>
int main(int argc, char **argv) {
int parity = 0;
int x;
while (std::cin >> x)
parity ^= x;
std::cout << parity << std::endl;
return 0;
}
إصدار scanf:
#include <stdio.h>
int main(int argc, char **argv) {
int parity = 0;
int x;
while (1 == scanf("%d", &x))
parity ^= x;
printf("%d\n", parity);
return 0;
}
النتائج
باستخدام برنامج ثالث ، قمت بإنشاء ملف نصي يحتوي على 33280276 رقم عشوائي. أوقات التنفيذ هي:
iostream version: 24.3 seconds
scanf version: 6.4 seconds
لا يبدو أن تغيير إعدادات تحسين برنامج التحويل البرمجي يغير النتائج كثيرًا على الإطلاق.
وبالتالي: هناك حقا فرق السرعة.
EDIT: المستخدم clyfish يشير أدناه أن اختلاف السرعة يرجع إلى حد كبير إلى وظائف I/O iostream التي تحافظ على التزامن مع وظائف C I/O. يمكننا إيقاف هذا عن طريق مكالمة إلى std::ios::sync_with_stdio(false);
:
#include <iostream>
int main(int argc, char **argv) {
int parity = 0;
int x;
std::ios::sync_with_stdio(false);
while (std::cin >> x)
parity ^= x;
std::cout << parity << std::endl;
return 0;
}
نتائج جديدة:
iostream version: 21.9 seconds
scanf version: 6.8 seconds
iostream with sync_with_stdio(false): 5.5 seconds
C++ iostream يفوز! اتضح أن هذا المزامنة الداخلية/التنظيف هو ما يبطئ عادة iostream i/o. إذا لم نقم بخلط stdio و iostream ، فيمكننا إيقاف تشغيله ، ثم iostream هو الأسرع.
http://www.quora.com/Is-cin-cout-slower-than-scanf-printf/answer/Aditya-Vishwakarma
يمكن أن يكون أداء cin
/cout
بطيئًا لأنهم بحاجة إلى الحفاظ على التزامن مع مكتبة C الأساسية. يعد هذا ضروريًا إذا كان سيتم استخدام كل من C IO و C++ IO.
ومع ذلك ، إذا كنت ستستخدم فقط C++ IO ، فببساطة استخدم السطر أدناه قبل أي عمليات IO.
std::ios::sync_with_stdio(false);
لمزيد من المعلومات حول هذا الموضوع ، انظر إلى مستندات libstdc ++ المقابلة .
ربما يكون scanf أسرع إلى حد ما من استخدام التدفقات. على الرغم من أن التدفقات توفر كثيرًا من أمان النوع ، وليس من الضروري تحليل سلاسل التنسيق في وقت التشغيل ، إلا أنها تتمتع عادة بميزة عدم طلب تخصيصات ذاكرة زائدة (يعتمد هذا على برنامج التحويل البرمجي ووقت التشغيل). ومع ذلك ، ما لم يكن الأداء هو هدفك الوحيد وأنت في المسار الحرج ، فعليك حقًا أن تفضل الأساليب الأكثر أمانًا (الأبطأ).
يوجد مقال لذيذ جدًا كتبه Herb Sutter " The String Formers of Manor Farm " الذي يتناول الكثير من التفاصيل عن أداء منسقي الأوتار مثل sscanf
و lexical_cast
وأي نوع من الأشياء يجعلهم يركضون ببطء أو بسرعة. هذا نوع من التشابه ، ربما لنوع الأشياء التي قد تؤثر على الأداء بين النمط C IO ونمط C++. تميل الاختلاف الرئيسي مع التنسيقات إلى أمان النوع وعدد عمليات تخصيص الذاكرة.
لقد أمضيت للتو أمسية في العمل على مشكلة على UVa Online (Factovisors ، مشكلة مثيرة جدًا للاهتمام ، راجعها):
كنت أحصل على TLE (تم تجاوز الحد الزمني) على عمليات التقديم الخاصة بي. في هذه المشكلات المتعلقة بحل مواقع المحكمين على الإنترنت ، يوجد لديك حوالي 2-3 من الوقت المحدد للتعامل مع آلاف حالات الاختبار المحتمل استخدامها لتقييم حلك. بالنسبة للمشاكل الكثيرة الحسابية مثل هذه المشكلة ، يتم حساب كل ميكروثانية.
كنت أستخدم الخوارزمية المقترحة (اقرأ عنها في منتديات المناقشة للموقع) ، لكنني ما زلت أحصل على TLEs.
لقد قمت فقط بتغيير "cin >> n >> m" إلى "scanf ("٪ d٪ d "، & n ، & m)" وعدد قليل من "couts" الصغيرة إلى "printfs" ، وتحولت TLE إلى "Accepted"!
لذلك ، نعم ، يمكن أن تحدث فرقًا كبيرًا ، خاصةً عندما تكون الحدود الزمنية قصيرة.
إذا كنت تهتم بالأداء وتنسيق السلسلة ، فقم بإلقاء نظرة على مكتبة Matthew Wilson's FastFormat .
تحرير - رابط لنشر accu في تلك المكتبة: http://accu.org/index.php/journals/1539
نعم iostream أبطأ من cstdio.
نعم ، ربما لا ينبغي أن تستخدم cstdio إذا كنت تتطور في C++.
بعد قولي هذا ، هناك طرق أسرع للحصول على I/O من scanf إذا كنت لا تهتم بالتنسيق ، اكتب الأمان ، بلاه ، بلاه ، بلاه ...
على سبيل المثال ، هذا روتين مخصص للحصول على رقم من STDIN:
inline int get_number()
{
int c;
int n = 0;
while ((c = getchar_unlocked()) >= '0' && c <= '9')
{
// n = 10 * n + (c - '0');
n = (n << 3) + ( n << 1 ) + c - '0';
}
return n;
}
هناك تطبيقات stdio ( libio ) والتي تنفذ FILE * كـ دفق C++ ، و fprintf كمحلل لتنسيق وقت التشغيل. لا تحتاج IOstreams إلى تحليل تنسيق وقت التشغيل ، كل هذا يتم في وقت الترجمة. لذلك ، مع تقاسم الخلفية ، فمن المعقول أن نتوقع أن iostreams أسرع في وقت التشغيل.
تكمن المشكلة في أن cin
لديه الكثير من الحملات الضمنية لأنه يمنحك طبقة تجريدية أعلى مكالمات scanf()
. يجب ألا تستخدم scanf()
over cin
إذا كنت تكتب برنامج C++ لأن هذا مطلوب cin
. إذا كنت تريد الأداء ، فربما لن تكتب I/O في C++ على أي حال.
يبدو أن عبارات cin
و cout
بشكل عام أبطأ من scanf
printf
في C++ ، لكنهما في الواقع أسرع!
الشيء هو: في C++ ، كلما استخدمت cin
و cout
، تتم عملية المزامنة بشكل افتراضي للتأكد من أنه إذا كنت تستخدم scanf
و cin
في البرنامج ، فهما يعملان متزامنين مع بعضهما البعض. تستغرق عملية المزامنة هذه وقتًا. وبالتالي cin
و cout
الظهور ليكون أبطأ.
ومع ذلك ، إذا تم تعيين عملية المزامنة على عدم حدوثها ، يكون cin
أسرع من scanf
.
لتخطي عملية المزامنة ، قم بتضمين مقتطف الشفرة التالي في البرنامج مباشرةً في بداية main()
:
std::ios::sync_with_stdio(false);
زيارة هذا الموقع لمزيد من المعلومات.
#include <stdio.h>
#include <unistd.h>
#define likely(x) __builtin_expect(!!(x), 1)
#define unlikely(x) __builtin_expect(!!(x), 0)
static int scanuint(unsigned int* x)
{
char c;
*x = 0;
do
{
c = getchar_unlocked();
if (unlikely(c==EOF)) return 1;
} while(c<'0' || c>'9');
do
{
//*x = (*x<<3)+(*x<<1) + c - '0';
*x = 10 * (*x) + c - '0';
c = getchar_unlocked();
if (unlikely(c==EOF)) return 1;
} while ((c>='0' && c<='9'));
return 0;
}
int main(int argc, char **argv) {
int parity = 0;
unsigned int x;
while (1 != (scanuint(&x))) {
parity ^= x;
}
parity ^=x;
printf("%d\n", parity);
return 0;
}
يوجد خطأ في نهاية الملف ، ولكن رمز C هذا أسرع بشكل كبير من إصدار C++ الأسرع.
[email protected] 3845568-78602a3f95902f3f3ac63b6beecaa9719e28a6d6 ▶ make test
time ./xor-c < Rand.txt
360589110
real 0m11,336s
user 0m11,157s
sys 0m0,179s
time ./xor2-c < Rand.txt
360589110
real 0m2,104s
user 0m1,959s
sys 0m0,144s
time ./xor-cpp < Rand.txt
360589110
real 0m29,948s
user 0m29,809s
sys 0m0,140s
time ./xor-cpp-noflush < Rand.txt
360589110
real 0m7,604s
user 0m7,480s
sys 0m0,123s
استغرق C++ الأصلي 30sec استغرق C رمز 2sec.