Re: About describe doubt for libev release ..
My personal reason for replying to your email so late! I am very sorry. Thank you for pointing out the problem of the mail format. Currently I use a browser to send mail. This format is likely to cause special characters to escape. I think I will try to improve it. --- I simulated the ev_once method in my own way, and cached the io watcher and timer watcher objects to achieve more efficient (memory) reuse; this can be seen from some test cases I wrote for this. The "memory stability" aspect does work really well. The project in my production environment needs to run at least 5000 requests / response scenarios per second (single process / single thread). The start and stop operations of this frequency are not expensive for an O(log n) operation, but In the "Flame Graph", its calling frequency ranks in the forefront. Although I haven't encountered any real performance doubts (not encountered yet), At present, I have completely disabled (can enable) the timer watcher, the purpose is to reduce the creation of a large number of timers and make it more suitable for the connection pooling characteristics of the load balancing software. It is mainly the above-mentioned "foresight" (perhaps stupid), and the analysis of the frequency of calls according to the method of "flame graph" makes me doubt about its practicality. (Please don't blame it) :) My idea: if there is no "painless" O(1) complexity operation, then I will try to minimize the coexistence of O(log n) complexity operations. --- Currently, I use ibev as the underlying event driver to drive a scripting language (Lua) to complete business logic. At the beginning, I considered the "minimum heap and time wheel" approach, but the actual performance is even worse. The reasons are as follows: 1. The interaction overhead of the "glue layer" between the two languages will increase, making me unable to determine whether it will work better. 2. The native runtime overhead of the lua language is 30 times slower than that of the C language, which makes me not necessarily improve the efficiency even when completed in the lua layer; --- I understand very well that you want to make libev more general, but sometimes there are always "choices". I think this may have a bad impact on your optimization of libev. At the same time, I very much hope that libev will often have some "breakthrough" features to better give users some "surprises". :) --- Finally, I have started porting libeio to my web development framework, and I have begun to perform stability tests on this "feature" in a test environment. I hope you can "clearly" release a stable version of libeio instead of letting us get it from "cvs". Known issues: 1. Simultaneously generating a large number of eio_req causes lock contention overhead of multiple threads than a single thread to complete synchronous I / O operations will aggravate the deterioration of CPU usage; (maybe I use it incorrectly) 2. The file descriptor (fd) created (obtained) by the fileno (FILE * f) method cannot complete the "cache" flush of setvbuf to disk using * sync; (maybe I used it incorrectly) 3. In the msys2 (cygwin) simulation environment, libeio fails to control the number of threads; ___ libev mailing list libev@lists.schmorp.de http://lists.schmorp.de/mailman/listinfo/libev
Re: About describe doubt for libev release ..
On Mon, Mar 23, 2020 at 02:44:58AM +0800, CandyMi <869646...@qq.com> wrote: > "While other data structures are possible and I vaguely plan some minor > optimisations" > > I'm very happy to hear what you said! Because every optimization of > Timer makes it easy for developers to use without having to hold hands in > some cases (I wrote a lot of code for this). > I mentioned in my email last year: "About the choice of the data > structure of the timer watcher". But I didn't get a reply, I want to > communicate with you about this possibility here. First of all, your mails arrive very garbled here, as if you mailer sent html instead of text - could you work on improving that= That makes reading and understanding your mails unnecessarily hard. As for optimisations, there is a limit to that - libev cannot know how your timers are used, and trying ti find out will slow it down so much that it isn't worth optimizing for many important cases. The biggest savings are to be done on the application side. > Of course, there is a simpler "ev_once" available. But when managing > more than 100,000 network connections, the overhead of the data structure > itself already occupies a very high user space. That's a very good example - ev_once does a (costly) malloc each time, and there is no good way aorund that (it could speed up the allocation, but it will have to allocate). Real savings can be done on the application side, by arranging its own data structures in a way that avoids extra memory allocastions altogether. > My current solution is the same as what you said: "Once you need to > stop the timer, mark it in timer- data!", Wait for the real timeout > before calling ev_timer_stop to stop it. That's not what I said (in fact, I personally do not use the data field very often, and wish there was a nice way to get rid of it, but there isn't, unless you compile libev yourself). What I meant is more clearly explained in http://pod.tst.eu/http://cvs.schmorp.de/libev/ev.pod#Be_smart_about_timeouts, part 3 (Let the timer time out, but then re-arm it as required.) - basically, if you have an inactivity timeout of, say, 60 seconds, you can have a timer that runs at least every 60 seconds, and then calculates the real timeout. That way, when there is activity (typically much more often than once per minute), you only need to do a single assignment instead of rearming the timer. And that needs to be done on the application side. If you have many sockets with identical timeouts, you can be much more efficient. For example, you could have a list sorted by activity and a single timer for all connections. And if you don't need super-exact timeout, but have lots of connections, you could have a timer that runs every n seconds and reaps connections. A generic event library can't know this and/or do this for you. I did play with the thought of having many more watcher types, such as a watcher type that has it's own container in which all timers have the same timeout, which could be used for that, but that was when I was under the influence of Perl's Event module API, and in the end, the more minimal libevent way of doing things won over. Most importantly, libev tries to allow applications to do all of this themselves without forcing them into a particular style, which isd what I learned from other event loops which aren't as generic. > if (timer-data-closed == 1) { >ev_timer_again(...); // only need to stop timer. >return ; > } > > timer-data-closed = 1; // now That doesn't seem like an advantage - if you know the timer is no longer needed, it should be cheaper to stop it directly and be done with it. > Can libev provide a "painless" way for developers to use > "ev_timer_start" at will without having to consider using "trick" to simplify > the complexity of the algorithm? I think it already does, to the extent possible. I think your specific trick is actually malking it worse, but I could be convinced on the opposite by a benchmark :) But I suspect the conditions under which it could be better would have to be very specific. The reason why it is likely worse is that a lot of timers increase the timer heap size, and effectively the same work has to be done as when calling ev_io_stop, plus extra work because the heap is larger and the operations likely have worse cache locality. > I have observed that a timer called "Timer-wheel" is provided in the > Linux kernel, and its algorithm complexity is currently constant. This may be > a good reference. I know how the kernel does it - however, while it suits the kernel, it cannot emulate libev timers, and just because it might be O(1) on some operations does not mean it is faster than a good heap. And most importantly, you can't have both a timer wheel (which requires your application to design aorund it) and a timer implementation that just works for every application (as in libev), i.e. your goal of being generic and easy to use directly
Re: About describe doubt for libev release ..
"While other data structures are possible and I vaguely plan some minor optimisations" I'm very happy to hear what you said! Because every optimization of Timer makes it easy for developers to use without having to hold hands in some cases (I wrote a lot of code for this). I mentioned in my email last year: "About the choice of the data structure of the timer watcher". But I didn't get a reply, I want to communicate with you about this possibility here. In general, we will use the following code to reflect when a network connection should be closed: static void timer_cb(...) { ev_io_stop(...); } static void io_cb(...) { ev_timer_stop(...); } static void listet_socket (int sock, int events, int timeout, int repeats, void* data) { io-data = data; ev_io_init(io, sock, io_cb, READ or write); ev_io_start(loop, io); timer-data = data; ev_timer_init(timer, timer_cb, timeout, repeats); ev_timer_start(loop, io); } Of course, there is a simpler "ev_once" available. But when managing more than 100,000 network connections, the overhead of the data structure itself already occupies a very high user space. My current solution is the same as what you said: "Once you need to stop the timer, mark it in timer- data!", Wait for the real timeout before calling ev_timer_stop to stop it. The modified code looks like this: static void timer_cb(...) { if (timer-data-closed == 1) { ev_timer_again(...); // only need to stop timer. return ; } ev_timer_again(...); ev_io_stop(...); ... } static void io_cb(...) { // ev_timer_stop(...); before timer-data-closed = 1; // now .. } But obviously, this makes the code we write gradually less and less readable. Can libev provide a "painless" way for developers to use "ev_timer_start" at will without having to consider using "trick" to simplify the complexity of the algorithm? I have observed that a timer called "Timer-wheel" is provided in the Linux kernel, and its algorithm complexity is currently constant. This may be a good reference. Have you considered optimizing by changing the data structure, or do you still have other "killer" ways to optimize? Will this affect the stability? ___ libev mailing list libev@lists.schmorp.de http://lists.schmorp.de/mailman/listinfo/libev
Re: About describe doubt for libev release ..
On Sun, Mar 22, 2020 at 12:56:16PM +0800, CandyMi <869646...@qq.com> wrote: > I have the same opinion about Linux aio and io_uring. The performance of aio > is not as good and problematic as described, but the use of io_uring may be > limited by the version of the Linux kernel and it makes me hesitant. > > There may be very few people who follow up on Linux 4.x / 5.x aggressively, > that is to say, there will be very few people who actually use it! And I also > have to maintain the old 2.6.32 kernel project). Actually, I would assume most people are now on a 4.x kernel, but of course, select and poll keep being supported and lubev will fall back to these if required. > The following code is my implementation of the IO watcher wrapper. The only > difference is: "the IO watcher passed to the core IO init method may have > called the core IO stop multiple times (object reuse). Such behavior is to > reduce the frequent creation or destruction of IO watcher." This got rather garbled, but I have one comment: > core_io_stop(core_loop *loop, core_io *io){ > if (io-events || io-fd){ If you want to check whether a watche ris active (strated), you could use ev_is_active(w). Your current if has the disadvantage of not stopping all acive watchers, as 0 is a valid value for both fd and events. > io-fd = io-events = 0x0; Also, after stopping, you can reuse the watche rmemory in any way you like, but you then cannot start it again without calling ev_io_init. > It is worth mentioning the use of the ev_timer_again method: "The consequence > of modifying timer- repeat is that it will cause the min-heap to be > adjusted every time. Will frequent use have a certain impact or even worse > performance?", Because The implementation of ev_timer_again is this (v4.25): While other data structures are possible, and I vaguely plan some minor optimisations, when you use ev_timer_again, libev of course has to adjust the heap to reflect the new reality. ev_timer_again is typically faster than a stop/start though, which is why it exists. Depending on your needs, you may be able to further optimize timers by not updating them at all on every change (e.g. for network timeouts, letting them expire and reschedule to the real timeout in the callback), or by creating your own data structure, e.g. if you have a lot of timers with the same timeout, you can put them into a linked list and only create a real timer for the next timer in that list, adding new timers to th end and so on. > Recently, I was watching the code of libeio. When will libeio be ready to > release the official version? I can call it an official version right now and create a CVS tag if that helps you. The main blocking issue is probably the lack of documentation, and some doubts on whether this is really the right API. And of course, libeio might profit most from io_uring. In terms of stability, libeio is pretty much stable and in maintenance for a decade now. -- The choice of a Deliantra, the free code+content MORPG -==- _GNU_ http://www.deliantra.net ==-- _ generation ---==---(_)__ __ __ Marc Lehmann --==---/ / _ \/ // /\ \/ / schm...@schmorp.de -=/_/_//_/\_,_/ /_/\_\ ___ libev mailing list libev@lists.schmorp.de http://lists.schmorp.de/mailman/listinfo/libev
Re: About describe doubt for libev release ..
thank you for your reply. I have the same opinion about Linux aio and io_uring. The performance of aio is not as good and problematic as described, but the use of io_uring may be limited by the version of the Linux kernel and it makes me hesitant. There may be very few people who follow up on Linux 4.x / 5.x aggressively, that is to say, there will be very few people who actually use it! And I also have to maintain the old 2.6.32 kernel project). ... The following code is my implementation of the IO watcher wrapper. The only difference is: "the IO watcher passed to the core IO init method may have called the core IO stop multiple times (object reuse). Such behavior is to reduce the frequent creation or destruction of IO watcher." #define CORE_LOOP core_default_loop() /* === Timer === */ void core_timer_init(core_timer *timer, _TIMER_CB cb){ timer-repeat = timer-at = 0x0; ev_init(timer, cb); } void core_timer_start(core_loop *loop, core_timer *timer, ev_tstamp timeout){ timer-repeat = timeout; ev_timer_again(loop ? loop : CORE_LOOP, timer); } void core_timer_stop(core_loop *loop, core_timer *timer){ timer-repeat = timer-at = 0x0; ev_timer_again(loop ? loop : CORE_LOOP, timer); } /* === Timer === */ /* === IO === */ void core_io_init(core_io *io, _IO_CB cb, int fd, int events){ ev_io_init(io, cb, fd, events); } void core_io_start(core_loop *loop, core_io *io){ ev_io_start(loop ? loop : CORE_LOOP, io); } void core_io_stop(core_loop *loop, core_io *io){ if (io-events || io-fd){ ev_io_stop(loop ? loop : CORE_LOOP, io); io-fd = io-events = 0x0; } } /* === IO === */ Even though I don't think they will adversely affect what you said, for security reasons I will put the code up to discuss with you and ask if there is an optimization solution. It is worth mentioning the use of the ev_timer_again method: "The consequence of modifying timer- repeat is that it will cause the min-heap to be adjusted every time. Will frequent use have a certain impact or even worse performance?", Because The implementation of ev_timer_again is this (v4.25): noinline void ev_timer_again (EV_P_ ev_timer *w) EV_NOEXCEPT { EV_FREQUENT_CHECK; clear_pending (EV_A_ (W)w); if (ev_is_active (w)) { if (w-repeat) { ev_at (w) = mn_now + w-repeat; ANHE_at_cache (timers [ev_active (w)]); adjustheap (timers, timercnt, ev_active (w)); } else ev_timer_stop (EV_A_ w); } else if (w-repeat) { ev_at (w) = w-repeat; ev_timer_start (EV_A_ w); } EV_FREQUENT_CHECK; } The core_timer_* is packaged so that it is suitable for one-time / cyclic "timer", and it does not need to apply for and release memory again and reuse it until the program ends(Like core_io_*). Recently, I was watching the code of libeio. When will libeio be ready to release the official version?___ libev mailing list libev@lists.schmorp.de http://lists.schmorp.de/mailman/listinfo/libev
Re: About describe doubt for libev release ..
> 1. "the documentation wrongly claimed that user may modify fd > and eventsmembers in io watchers when the watcher was stopped." means: > Can't modify internal members even if I/O watcher has been stopped? Yes - you have to use ev_io_set or the new ev_io_modify, direct modification does not work properly as the io watcher caches some kernel state info. If you modified these members directly, it might or might not work correctly, even in old versions. To be specific, in most versions of libev, I/O watchers assume that if the fd wasn't changed via ev_io_set, it will refer to the same underlying file description, unless you use ev_io_set. If you modify the fd directly, you might not receive events for it, and you might even receive events for fds that are no longer open (mostly due to the braindamage that is epoll). > 2. What is the scenario where timerfd checks "Time Jump" cannot > wake up? why a minute? I don't know of any scenarion where timerfds fail - I assume you refer to something in the Changes file, but what exactly? The only thing that changed is that libev does not wake up roughly every minute when it is sure it can keep the time without it. > 3. What are the special reasons why Linux AIO is no longer the > default backend? It never was the default backend, it merely was available for use by default. This is no longer the case - compiled libev versions no longer support the aio backend by default. The reasons are not that special: linux aio is buggy, extremely slow and suffers from a multitude of arbitrary limitations, which is why it couldn't be a default backend. It is expected that the io_uring interface will eventually replace the epoll backend, although at the moment it's not there yet. So basically, it is useless dead code that is of little to no use to anybody, and would only bloat the binary. -- The choice of a Deliantra, the free code+content MORPG -==- _GNU_ http://www.deliantra.net ==-- _ generation ---==---(_)__ __ __ Marc Lehmann --==---/ / _ \/ // /\ \/ / schm...@schmorp.de -=/_/_//_/\_,_/ /_/\_\ ___ libev mailing list libev@lists.schmorp.de http://lists.schmorp.de/mailman/listinfo/libev