ন্যূনতম চলমান POSIX সি উদাহরণ
জিনিসগুলিকে আরও কংক্রিট করার জন্য, আমি time
কয়েকটি ন্যূনতম সি পরীক্ষা প্রোগ্রামের সাথে কয়েকটি চরম মামলার উদাহরণ দিতে চাই ।
সমস্ত প্রোগ্রাম সংকলিত এবং এর সাথে চালানো যেতে পারে:
gcc -ggdb3 -o main.out -pthread -std=c99 -pedantic-errors -Wall -Wextra main.c
time ./main.out
এবং উবুন্টু 18.10, জিসিসি 8.2.0, গ্লিবসি 2.28, লিনাক্স কার্নেল 4.18, থিঙ্কপ্যাড পি 51 ল্যাপটপ, ইন্টেল কোর আই 7-7820 এইচকিউ সিপিইউ (4 কোর / 8 থ্রেড), 2 এক্স স্যামসুং এম 471 এ 2 কে 43 বিবি 1-সিআরসি র্যাম (2x 16 জিবিবি) পরীক্ষা করা হয়েছে।
ঘুম
অ-ব্যস্ত ঘুম কেবল user
বা sys
কেবলমাত্র গণনা করে না real
।
উদাহরণস্বরূপ, এমন একটি প্রোগ্রাম যা সেকেন্ডের জন্য ঘুমায়:
#define _XOPEN_SOURCE 700
#include <stdlib.h>
#include <unistd.h>
int main(void) {
sleep(1);
return EXIT_SUCCESS;
}
গিটহাব উজানের দিকে ।
ফলাফল কিছু:
real 0m1.003s
user 0m0.001s
sys 0m0.003s
আইও উপলব্ধ হয়ে ওঠার জন্য অবরুদ্ধ কর্মসূচিগুলির জন্য একই ধার্য।
উদাহরণস্বরূপ, নিম্নলিখিত প্রোগ্রামটি ব্যবহারকারী একটি অক্ষর প্রবেশ করানোর জন্য অপেক্ষা করে এবং এন্টার টিপুন:
#include <stdio.h>
#include <stdlib.h>
int main(void) {
printf("%c\n", getchar());
return EXIT_SUCCESS;
}
গিটহাব উজানের দিকে ।
এবং যদি আপনি প্রায় এক সেকেন্ডের জন্য অপেক্ষা করেন, তবে এটি ঘুমের উদাহরণের মতো ফলাফল দেয়:
real 0m1.003s
user 0m0.001s
sys 0m0.003s
এই কারণে time
আপনাকে সিপিইউ এবং আইও বাউন্ড প্রোগ্রামগুলির মধ্যে পার্থক্য করতে সহায়তা করতে পারে: "সিপিইউ বাউন্ড" এবং "আই / ও বাউন্ড" শব্দের অর্থ কী?
একাধিক থ্রেড
নিম্নলিখিত উদাহরণটি থ্রেডগুলিতে niters
বেহুদা বিশুদ্ধ সিপিইউযুক্ত কাজের পুনরাবৃত্তি করে nthreads
:
#define _XOPEN_SOURCE 700
#include <assert.h>
#include <inttypes.h>
#include <pthread.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
uint64_t niters;
void* my_thread(void *arg) {
uint64_t *argument, i, result;
argument = (uint64_t *)arg;
result = *argument;
for (i = 0; i < niters; ++i) {
result = (result * result) - (3 * result) + 1;
}
*argument = result;
return NULL;
}
int main(int argc, char **argv) {
size_t nthreads;
pthread_t *threads;
uint64_t rc, i, *thread_args;
/* CLI args. */
if (argc > 1) {
niters = strtoll(argv[1], NULL, 0);
} else {
niters = 1000000000;
}
if (argc > 2) {
nthreads = strtoll(argv[2], NULL, 0);
} else {
nthreads = 1;
}
threads = malloc(nthreads * sizeof(*threads));
thread_args = malloc(nthreads * sizeof(*thread_args));
/* Create all threads */
for (i = 0; i < nthreads; ++i) {
thread_args[i] = i;
rc = pthread_create(
&threads[i],
NULL,
my_thread,
(void*)&thread_args[i]
);
assert(rc == 0);
}
/* Wait for all threads to complete */
for (i = 0; i < nthreads; ++i) {
rc = pthread_join(threads[i], NULL);
assert(rc == 0);
printf("%" PRIu64 " %" PRIu64 "\n", i, thread_args[i]);
}
free(threads);
free(thread_args);
return EXIT_SUCCESS;
}
গিটহাব আপস্ট্রিম + প্লট কোড ।
তারপরে আমরা আমার 8 হাইপারথ্রেড সিপিইউতে স্থির 10 ^ 10 পুনরাবৃত্তির জন্য থ্রেডের সংখ্যার ফাংশন হিসাবে প্রাচীর, ব্যবহারকারী এবং সিস প্লট করি:
প্লট ডেটা ।
গ্রাফ থেকে আমরা দেখতে পাচ্ছি:
সিপিইউ নিবিড় একক কোর অ্যাপ্লিকেশনটির জন্য, প্রাচীর এবং ব্যবহারকারী প্রায় একই রকম
2 টি কোরের জন্য ব্যবহারকারী প্রায় 2x প্রাচীর, যার অর্থ ব্যবহারকারীর সময়টি সমস্ত থ্রেডে গণনা করা হয়।
ব্যবহারকারী মূলত দ্বিগুণ, এবং প্রাচীর একই থাকাকালীন।
এটি 8 টি থ্রেড পর্যন্ত অব্যাহত থাকে যা আমার কম্পিউটারে আমার হাইপারথ্রেডগুলির সংখ্যার সাথে মেলে।
8 এর পরে, প্রাচীরটিও বাড়তে শুরু করে, কারণ নির্দিষ্ট সময় মতো আরও কাজ করার জন্য আমাদের কোনও অতিরিক্ত সিপিইউ নেই!
এই সময়ে অনুপাতের মালভূমি।
মনে রাখবেন যে এই গ্রাফটি কেবলমাত্র পরিষ্কার এবং সহজ কারণ কাজটি সম্পূর্ণ সিপিইউ-বাউন্ডড: যদি এটি মেমরির সীমাবদ্ধ থাকে তবে আমরা কম কোর দিয়ে পারফরম্যান্সে অনেক আগেই পতিত হব কারণ মেমরির অ্যাকসেসগুলি কী হিসাবে দেখানো হয়েছে তেমন একটি বাধা হতে পারে What "সিপিইউ বাউন্ড" এবং "আই / ও বাউন্ড" শব্দগুলি কি বোঝায়?
সঙ্গে ভারী কাজ sys sendfile
আমি যে ভারীতম কাজের কাজের চাপটি নিয়ে আসতে পারি তা হ'ল sendfile
কার্নেল স্পেসে একটি ফাইল অনুলিপি অপারেশন করে: একটি ফাইলকে বুদ্ধিমান, নিরাপদ এবং দক্ষ উপায়ে অনুলিপি করুন
সুতরাং আমি কল্পনা করেছি যে এই ইন-কার্নেলটি memcpy
সিপিইউ নিবিড় অপারেশন হবে।
প্রথমে আমি একটি বৃহত 10GiB এলোমেলো ফাইলটি এর সাথে শুরু করি:
dd if=/dev/urandom of=sendfile.in.tmp bs=1K count=10M
তারপরে কোডটি চালান:
#define _GNU_SOURCE
#include <assert.h>
#include <fcntl.h>
#include <stdlib.h>
#include <sys/sendfile.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>
int main(int argc, char **argv) {
char *source_path, *dest_path;
int source, dest;
struct stat stat_source;
if (argc > 1) {
source_path = argv[1];
} else {
source_path = "sendfile.in.tmp";
}
if (argc > 2) {
dest_path = argv[2];
} else {
dest_path = "sendfile.out.tmp";
}
source = open(source_path, O_RDONLY);
assert(source != -1);
dest = open(dest_path, O_WRONLY | O_CREAT | O_TRUNC, S_IRUSR | S_IWUSR);
assert(dest != -1);
assert(fstat(source, &stat_source) != -1);
assert(sendfile(dest, source, 0, stat_source.st_size) != -1);
assert(close(source) != -1);
assert(close(dest) != -1);
return EXIT_SUCCESS;
}
গিটহাব উজানের দিকে ।
যা প্রত্যাশা অনুযায়ী মূলত সিস্টেম সময় দেয়:
real 0m2.175s
user 0m0.001s
sys 0m1.476s
আমি জানতে আগ্রহী ছিলাম যে time
বিভিন্ন প্রক্রিয়ার সিস্টেমে কল করার মধ্যে পার্থক্য আছে কি না, তাই আমি চেষ্টা করেছিলাম:
time ./sendfile.out sendfile.in1.tmp sendfile.out1.tmp &
time ./sendfile.out sendfile.in2.tmp sendfile.out2.tmp &
এবং ফলাফল ছিল:
real 0m3.651s
user 0m0.000s
sys 0m1.516s
real 0m4.948s
user 0m0.000s
sys 0m1.562s
সিস্টেমে সময় একক প্রক্রিয়া হিসাবে উভয়ের জন্য একই, কিন্তু প্রাচীর সময় বৃহত্তর কারণ প্রক্রিয়া ডিস্ক রিড অ্যাক্সেস সম্ভবত প্রতিযোগিতা হয়।
সুতরাং মনে হচ্ছে এটি বাস্তবে অ্যাকাউন্টের জন্য রয়েছে যার জন্য কোনও প্রদত্ত কার্নেল কাজ শুরু করে।
বাশ সোর্স কোড
আপনি যখন কেবল time <cmd>
উবুন্টুতে কাজ করেন, এটি বাশ কীওয়ার্ডটি ব্যবহার করে যা দেখতে পাওয়া যায়:
type time
কোন ফলাফল:
time is a shell keyword
সুতরাং আমরা আউটপুট স্ট্রিংয়ের জন্য বাশ 4.19 উত্স কোডে উত্সটি গ্রেপ করি:
git grep '"user\b'
যা আমাদের এক্সিকিউট_সিএমডি.সি ফাংশনে নিয়ে যায় time_command
, যা ব্যবহার করে:
gettimeofday()
এবং getrusage()
যদি উভয় উপলব্ধ
times()
অন্যভাবে
যার সবগুলি লিনাক্স সিস্টেম কল এবং পসিক্স ফাংশন ।
GNU Coreutils উত্স কোড
যদি আমরা এটিকে কল করি:
/usr/bin/time
তারপরে এটি GNU Coreutils বাস্তবায়ন ব্যবহার করে।
এইটি কিছুটা জটিল, তবে প্রাসঙ্গিক উত্সটি resuse.c এ উপস্থিত বলে মনে হচ্ছে এবং এটি করে:
wait3
যদি পাওয়া যায় তবে একটি নন-পসিক্স বিএসডি কল
times
এবং gettimeofday
অন্যথায়