मैंने अपने कंप्यूटर पर एक उत्सुक चीज़ देखी। * हस्तलिखित विभाज्यता परीक्षण %
ऑपरेटर की तुलना में काफी तेज है । न्यूनतम उदाहरण पर विचार करें:
* एएमडी राईजन थ्रेडिपर 2990WX, GCC 9.2.0
static int divisible_ui_p(unsigned int m, unsigned int a)
{
if (m <= a) {
if (m == a) {
return 1;
}
return 0;
}
m += a;
m >>= __builtin_ctz(m);
return divisible_ui_p(m, a);
}
उदाहरण विषम a
और द्वारा सीमित है m > 0
। हालांकि, यह आसानी से सभी के लिए सामान्यीकृत किया जा सकता है a
और m
। कोड सिर्फ विभाजन को परिवर्धन की श्रृंखला में परिवर्तित करता है।
अब इस बात पर विचार करें कि परीक्षण कार्यक्रम किसके साथ संकलित है -std=c99 -march=native -O3
:
for (unsigned int a = 1; a < 100000; a += 2) {
for (unsigned int m = 1; m < 100000; m += 1) {
#if 1
volatile int r = divisible_ui_p(m, a);
#else
volatile int r = (m % a == 0);
#endif
}
}
... और मेरे कंप्यूटर पर परिणाम:
| implementation | time [secs] |
|--------------------|-------------|
| divisible_ui_p | 8.52user |
| builtin % operator | 17.61user |
इसलिए 2 गुना से अधिक तेजी से।
प्रश्न: क्या आप मुझे बता सकते हैं कि कोड आपके मशीन पर कैसे व्यवहार करता है? क्या यह जीसीसी में अनुकूलन अवसर चूक गया है? क्या आप यह परीक्षण और भी तेजी से कर सकते हैं?
अद्यतन: अनुरोध के रूप में, यहाँ एक न्यूनतम प्रतिलिपि प्रस्तुत करने योग्य उदाहरण है:
#include <assert.h>
static int divisible_ui_p(unsigned int m, unsigned int a)
{
if (m <= a) {
if (m == a) {
return 1;
}
return 0;
}
m += a;
m >>= __builtin_ctz(m);
return divisible_ui_p(m, a);
}
int main()
{
for (unsigned int a = 1; a < 100000; a += 2) {
for (unsigned int m = 1; m < 100000; m += 1) {
assert(divisible_ui_p(m, a) == (m % a == 0));
#if 1
volatile int r = divisible_ui_p(m, a);
#else
volatile int r = (m % a == 0);
#endif
}
}
return 0;
}
साथ gcc -std=c99 -march=native -O3 -DNDEBUG
AMD Ryzen Threadripper 2990WX पर संकलित
gcc --version
gcc (Gentoo 9.2.0-r2 p3) 9.2.0
UPDATE2: अनुरोध के अनुसार, वह संस्करण जो किसी को भी संभाल सकता है a
और m
(यदि आप पूर्णांक ओवरफ़्लो से बचना चाहते हैं, तो परीक्षण को पूर्णांक प्रकार के साथ दो बार इनपुट पूर्णांक के साथ लागू किया जाना है):
int divisible_ui_p(unsigned int m, unsigned int a)
{
#if 1
/* handles even a */
int alpha = __builtin_ctz(a);
if (alpha) {
if (__builtin_ctz(m) < alpha) {
return 0;
}
a >>= alpha;
}
#endif
while (m > a) {
m += a;
m >>= __builtin_ctz(m);
}
if (m == a) {
return 1;
}
#if 1
/* ensures that 0 is divisible by anything */
if (m == 0) {
return 1;
}
#endif
return 0;
}
r
कि आप जो दो गणना करते हैं, वे वास्तव में एक दूसरे के बराबर हैं।
a % b
है b
तुलना में काफी छोटा a
। आपके परीक्षण के मामले में अधिकांश पुनरावृत्तियों के माध्यम से, वे समान आकार के होते हैं, या b
बड़े होते हैं, और आपका संस्करण उन स्थितियों में कई सीपीयू पर तेज हो सकता है।