C++ 我能做这个C++;在不增加复杂性的情况下更快地编写代码?
我在一个编程问题网站(codechef.com)上解决了一个问题,以防有人在尝试之前不想看到这个解决方案。这用测试数据在5.43秒内解决了问题,其他人用相同的测试数据在0.14秒内解决了同样的问题,但代码要复杂得多。有人能指出我的代码中性能下降的特定区域吗?我仍然在学习C++,所以我知道有一百万种方法可以解决这个问题,但是我想知道我是否可以用一些微妙的改变来改进我自己的解决方案,而不是重写整个事情。或者,如果有任何相对简单的解决方案,它们的长度相当,但性能比我的好,我也有兴趣看看它们 请记住我正在学习C++,所以我的目标是改进我理解的代码,而不是仅仅给出一个完美的解决方案。 谢谢 问题: 此问题的目的是验证用于读取输入数据的方法是否足够快,以处理带有大量输入/输出警告的问题。您希望在运行时每秒至少能够处理2.5MB的输入数据。处理测试数据的时间限制为8秒 输入以两个正整数nk(n,k0;n--)开始 { scanf(“%i”、&inputnum); 如果(inputnum%k==0)总计+=1; } //总产值 printf(“%i”,总计); 返回0; }C++ 我能做这个C++;在不增加复杂性的情况下更快地编写代码?,c++,performance,C++,Performance,我在一个编程问题网站(codechef.com)上解决了一个问题,以防有人在尝试之前不想看到这个解决方案。这用测试数据在5.43秒内解决了问题,其他人用相同的测试数据在0.14秒内解决了同样的问题,但代码要复杂得多。有人能指出我的代码中性能下降的特定区域吗?我仍然在学习C++,所以我知道有一百万种方法可以解决这个问题,但是我想知道我是否可以用一些微妙的改变来改进我自己的解决方案,而不是重写整个事情。或者,如果有任何相对简单的解决方案,它们的长度相当,但性能比我的好,我也有兴趣看看它们 请记住我正
将两个大数字分开很难。也许一个改进是首先通过观察一些较小的素数来描述k。现在让我们说2、3和5。如果k可以被其中任何一个整除,那么inputnum也需要被整除,或者inputnum不能被k整除。当然还有更多的技巧(你可以使用bitwise and Of inputnum to 1来确定你是否可以被2整除),但我认为只要去掉低素数的可能性,速度就会得到合理的提高(无论如何值得一试)。尝试用
count+=((n%k)==0)替换if语句代码>。这可能会有点帮助
但我认为您确实需要将输入缓冲到临时数组中。一次从输入中读取一个整数是昂贵的。如果可以将数据采集和数据处理分开,编译器可能能够为数学运算生成优化的代码。I/O操作是瓶颈。尽可能地限制它们,例如一步将所有数据加载到缓冲区或带有缓冲流的数组中
虽然你的例子很简单,我几乎看不出你能消除什么——假设这是问题的一部分,那么接下来阅读stdin
对代码的一些注释:您的示例没有使用任何流-不需要包括iostream头。您已经将C库元素加载到全局命名空间,包括STDIO。H而不是C++版本的标题CSSTIO,因此不需要使用名称空间STD。 < P>可以尝试逐行读取输入,并使用ATOI()来输入每个行。这应该比scanf快一点,因为您消除了格式字符串的“扫描”开销。我认为代码很好。我用不到0.3秒的时间在我的电脑上运行了它
我甚至在不到一秒钟的时间里用更大的输入运行它
你是怎么定时间的
您可以做的一件小事是删除if语句。
从total=n开始,然后在循环内:
total-=int((输入%k)/k+1)//0如果可以整除,1如果不能整除速度不是由计算决定的程序运行所花费的大部分时间都被i/o消耗
在第一次scanf
之前添加调用,以获得显著改进:
setvbuf(stdin, NULL, _IOFBF, 32768);
setvbuf(stdout, NULL, _IOFBF, 32768);
--编辑--
所谓的幻数是新的缓冲区大小。默认情况下,文件使用512字节的缓冲区。增加这个大小会减少C++运行库向操作系统发出读写调用的次数,这是目前为止算法中最昂贵的操作。
通过将缓冲区大小保持为512的倍数,可以消除缓冲区碎片。大小应该是1024*10
还是1024*1024
取决于要在其上运行的系统。对于16位系统,大于32K或64K的缓冲区大小通常会导致分配缓冲区和管理缓冲区的困难。对于任何更大的系统,根据可用内存和它将要竞争的其他内容,使其尽可能大
如果没有任何已知的内存争用,请为缓冲区选择大约与关联文件大小相同的大小。也就是说,如果输入文件为250K,则将其用作缓冲区大小。随着缓冲区大小的增加,回报肯定会减少。对于250K示例,100K缓冲区需要三次读取,而默认512字节缓冲区需要500次读取。进一步增加缓冲区大小以便只需要一次读取不太可能比三次读取带来显著的性能改进。您可以使用gets()读取每一行,并在不使用scanf()的情况下自己解析字符串。(通常我不建议使用get(),但在本例中,输入是经过良好指定的。)
解决此问题的示例C程序:
#include <stdio.h>
int main() {
int n,k,in,tot=0,i;
char s[1024];
gets(s);
sscanf(s,"%d %d",&n,&k);
while(n--) {
gets(s);
in=s[0]-'0';
for(i=1; s[i]!=0; i++) {
in=in*10 + s[i]-'0'; /* For each digit read, multiply the previous
value of in with 10 and add the current digit */
}
tot += in%k==0; /* returns 1 if in%k is 0, 0 otherwise */
}
printf("%d\n",tot);
return 0;
}
#包括
int main(){
int n,k,in,tot=0,i;
chars[1024];
获取(s);
sscanf(s、%d%d、&n和&k);
而(n--){
获取(s);
in=s[0]-“0”;
对于(i=1;s[i]!=0;i++){
in=in*10+s[i]-“0”;/*对于读取的每个数字,乘以上一个数字
输入的值加上10,并将当前数字相加*/
}
%k中的tot+=0;/*如果%k中的值为0,则返回1,否则返回0*/
}
printf(“%d\n”,tot);
返回0;
}
此程序比您在上面给出的解决方案(在我的机器上)快约2.6倍。我在28311552行输入上测试了以下内容。它比你的代码快10倍。什么
#include <stdio.h>
int main() {
int n,k,in,tot=0,i;
char s[1024];
gets(s);
sscanf(s,"%d %d",&n,&k);
while(n--) {
gets(s);
in=s[0]-'0';
for(i=1; s[i]!=0; i++) {
in=in*10 + s[i]-'0'; /* For each digit read, multiply the previous
value of in with 10 and add the current digit */
}
tot += in%k==0; /* returns 1 if in%k is 0, 0 otherwise */
}
printf("%d\n",tot);
return 0;
}
[xavier:~/tmp] dalke% g++ -O3 my_solution.cpp
[xavier:~/tmp] dalke% time ./a.out < c.dat
15728647
0.284u 0.057s 0:00.39 84.6% 0+0k 0+1io 0pf+0w
[xavier:~/tmp] dalke% g++ -O3 your_solution.cpp
[xavier:~/tmp] dalke% time ./a.out < c.dat
15728647
3.585u 0.087s 0:03.72 98.3% 0+0k 0+0io 0pf+0w
#include <iostream>
#include <stdio.h>
using namespace std;
const int BUFFER_SIZE=400000;
const int EXTRA=30; // well over the size of an integer
void read_to_newline(char *buffer) {
int c;
while (1) {
c = getc_unlocked(stdin);
if (c == '\n' || c == EOF) {
*buffer = '\0';
return;
}
*buffer++ = c;
}
}
int main() {
char buffer[BUFFER_SIZE+EXTRA];
char *end_buffer;
char *startptr, *endptr;
//n is number of integers to perform calculation on
//k is the divisor
//inputnum is the number to be divided by k
//total is the total number of inputnums divisible by k
int n,k,inputnum,total,nbytes;
//initialize total to zero
total=0;
//read in n and k from stdin
read_to_newline(buffer);
sscanf(buffer, "%i%i",&n,&k);
while (1) {
// Read a large block of values
// There should be one integer per line, with nothing else.
// This might truncate an integer!
nbytes = fread(buffer, 1, BUFFER_SIZE, stdin);
if (nbytes == 0) {
cerr << "Reached end of file too early" << endl;
break;
}
// Make sure I read to the next newline.
read_to_newline(buffer+nbytes);
startptr = buffer;
while (n>0) {
inputnum = 0;
// I had used strtol but that was too slow
// inputnum = strtol(startptr, &endptr, 10);
// Instead, parse the integers myself.
endptr = startptr;
while (*endptr >= '0') {
inputnum = inputnum * 10 + *endptr - '0';
endptr++;
}
// *endptr might be a '\n' or '\0'
// Might occur with the last field
if (startptr == endptr) {
break;
}
// skip the newline; go to the
// first digit of the next number.
if (*endptr == '\n') {
endptr++;
}
// Test if this is a factor
if (inputnum % k==0) total += 1;
// Advance to the next number
startptr = endptr;
// Reduce the count by one
n--;
}
// Either we are done, or we need new data
if (n==0) {
break;
}
}
// output value of total
printf("%i\n",total);
return 0;
}
#include <windows.h>
#include <process.h>
#include <iostream>
#include <time.h>
#include "queue.hpp"
namespace jvc = JVC_thread_queue;
struct buffer {
static const int initial_size = 1024 * 1024;
char buf[initial_size];
size_t size;
buffer() : size(initial_size) {}
};
jvc::queue<buffer *> outputs;
void read(HANDLE file) {
// read data from specified file, put into buffers for processing.
//
char temp[32];
int temp_len = 0;
int i;
buffer *b;
DWORD read;
do {
b = new buffer;
// If we have a partial line from the previous buffer, copy it into this one.
if (temp_len != 0)
memcpy(b->buf, temp, temp_len);
// Then fill the buffer with data.
ReadFile(file, b->buf+temp_len, b->size-temp_len, &read, NULL);
// Look for partial line at end of buffer.
for (i=read; b->buf[i] != '\n'; --i)
;
// copy partial line to holding area.
memcpy(temp, b->buf+i, temp_len=read-i);
// adjust size.
b->size = i;
// put buffer into queue for processing thread.
// transfers ownership.
outputs.add(b);
} while (read != 0);
}
// A simplified istrstream that can only read int's.
class num_reader {
buffer &b;
char *pos;
char *end;
public:
num_reader(buffer *buf) : b(*buf), pos(b.buf), end(pos+b.size) {}
num_reader &operator>>(int &value){
int v = 0;
// skip leading "stuff" up to the first digit.
while ((pos < end) && !isdigit(*pos))
++pos;
// read digits, create value from them.
while ((pos < end) && isdigit(*pos)) {
v = 10 * v + *pos-'0';
++pos;
}
value = v;
return *this;
}
// return stream status -- only whether we're at end
operator bool() { return pos < end; }
};
int result;
unsigned __stdcall processing_thread(void *) {
int value;
int n, k;
int count = 0;
// Read first buffer: n & k followed by values.
buffer *b = outputs.pop();
num_reader input(b);
input >> n;
input >> k;
while (input >> value && ++count < n)
result += ((value %k ) == 0);
// Ownership was transferred -- delete buffer when finished.
delete b;
// Then read subsequent buffers:
while ((b=outputs.pop()) && (b->size != 0)) {
num_reader input(b);
while (input >> value && ++count < n)
result += ((value %k) == 0);
// Ownership was transferred -- delete buffer when finished.
delete b;
}
return 0;
}
int main() {
HANDLE standard_input = GetStdHandle(STD_INPUT_HANDLE);
HANDLE processor = (HANDLE)_beginthreadex(NULL, 0, processing_thread, NULL, 0, NULL);
clock_t start = clock();
read(standard_input);
WaitForSingleObject(processor, INFINITE);
clock_t finish = clock();
std::cout << (float)(finish-start)/CLOCKS_PER_SEC << " Seconds.\n";
std::cout << result;
return 0;
}
#ifndef QUEUE_H_INCLUDED
#define QUEUE_H_INCLUDED
namespace JVC_thread_queue {
template<class T, unsigned max = 256>
class queue {
HANDLE space_avail; // at least one slot empty
HANDLE data_avail; // at least one slot full
CRITICAL_SECTION mutex; // protect buffer, in_pos, out_pos
T buffer[max];
long in_pos, out_pos;
public:
queue() : in_pos(0), out_pos(0) {
space_avail = CreateSemaphore(NULL, max, max, NULL);
data_avail = CreateSemaphore(NULL, 0, max, NULL);
InitializeCriticalSection(&mutex);
}
void add(T data) {
WaitForSingleObject(space_avail, INFINITE);
EnterCriticalSection(&mutex);
buffer[in_pos] = data;
in_pos = (in_pos + 1) % max;
LeaveCriticalSection(&mutex);
ReleaseSemaphore(data_avail, 1, NULL);
}
T pop() {
WaitForSingleObject(data_avail,INFINITE);
EnterCriticalSection(&mutex);
T retval = buffer[out_pos];
out_pos = (out_pos + 1) % max;
LeaveCriticalSection(&mutex);
ReleaseSemaphore(space_avail, 1, NULL);
return retval;
}
~queue() {
DeleteCriticalSection(&mutex);
CloseHandle(data_avail);
CloseHandle(space_avail);
}
};
}
#endif
#define DIGIT(c)((c)>='0' && (c) <= '9')
bool parsInt(char* &p, int& num){
while(*p && *p <= ' ') p++; // scan over whitespace
if (!DIGIT(*p)) return false;
num = 0;
while(DIGIT(*p)){
num = num * 10 + (*p++ - '0');
}
return true;
}