- 投稿日:2020-07-11T23:10:41+09:00
QuarkusがJava Lambdaを救う!?
すごいですQuarkus!!!!!!
JavaのLambdaのコールドスタートがチョッパヤです!!以下の公式サイトを参考に試しました。
https://quarkus.io/guides/amazon-lambda#tracing-with-aws-xray-and-graalvm
テスト対象
前回のAWS LambdaのJavaは遅い?とほぼ同じですが、以下が異なります。いずれもQuarkusの制限?制約?です。
- アノテーションが付いてる
- httpClientがUrlConnectionHttpClient
ソース全体
package example; import javax.inject.Named; import com.amazonaws.services.lambda.runtime.Context; import com.amazonaws.services.lambda.runtime.RequestHandler; import software.amazon.awssdk.services.s3.S3Client; import software.amazon.awssdk.services.s3.model.PutObjectRequest; import software.amazon.awssdk.services.s3.model.PutObjectResponse; import software.amazon.awssdk.core.sync.RequestBody; import software.amazon.awssdk.regions.Region; @Named("test") public class TestLambda implements RequestHandler<Object, Object> { @Override public Object handleRequest(Object input, Context context) { String ENV_BUCKET = System.getenv("BUCKET"); S3Client s3 = S3Client.builder() .region(Region.AP_NORTHEAST_1) .httpClient(software.amazon.awssdk.http.urlconnection.UrlConnectionHttpClient.builder().build()) .build(); PutObjectResponse result = s3.putObject( PutObjectRequest.builder().bucket(ENV_BUCKET).key("filename.txt").build(), RequestBody.fromString("contents")); System.out.println(result); return ""; } }検証結果
回数 レイテンシ(ms) 処理内容 1 2700 2 250 3 305 4 319 5 187 前回はコールドスタートが6200msだったので、めちゃ早になりました!
検証結果(+Provisioned Concurrency)
こんなチョッパヤなQuarkusさんをProvisionedにしたらどうなるんでしょうね。ワクワクしますね。
回数 レイテンシ(ms) 処理内容 1 417 2 198 3 206 4 270 5 147 期待を裏切らない速さ!すごいよ、Quarkus!
参考
https://quarkus.io/guides/amazon-lambda#tracing-with-aws-xray-and-graalvm
https://aws.amazon.com/jp/blogs/architecture/field-notes-optimize-your-java-application-for-aws-lambda-with-quarkus/
- 投稿日:2020-07-11T21:57:32+09:00
【Java】Spigot プラグイン開発 - コマンドをバニラコマンドとして登録する
概要
・普通はplugin.ymlで登録するコマンドをバニラコマンドとして登録する。
対象読者
・プラグイン開発の環境が整っている。
環境
・Windows 10 - 1909
・Eclipse IDE Version: 2020-06 (4.16.0)
・Minecraft 1.16.1
・Spigot 1.16.1注意
ここでは、Minecraft 1.13から追加されたコマンドエンジン brigadier を使用するので、1.13より下のバージョンではできないかもしれません。
作成するコマンド
・今回は、無難に「プレイヤーを飛べるようにする」というコマンドを作成します。
・コマンドの構文
/fly <プレイヤー> allow
ソースコードと説明
・まずは、クラスを作成しましょう。
FlyCommand.javapackage com.hamusuke.qiita.command; import java.util.Collection; import com.mojang.brigadier.CommandDispatcher; import com.mojang.brigadier.builder.LiteralArgumentBuilder; import net.minecraft.server.v1_16_R1.ArgumentEntity; import net.minecraft.server.v1_16_R1.ChatMessage; import net.minecraft.server.v1_16_R1.CommandListenerWrapper; import net.minecraft.server.v1_16_R1.EntityPlayer; public class FlyCommand { public static void register(CommandDispatcher<CommandListenerWrapper> dispatcher) { LiteralArgumentBuilder<CommandListenerWrapper> literalArgumentBuilder = net.minecraft.server.v1_16_R1.CommandDispatcher.a("fly").requires((permission) -> { return permission.hasPermission(2); }).then(net.minecraft.server.v1_16_R1.CommandDispatcher.a("target", ArgumentEntity.d()).then(net.minecraft.server.v1_16_R1.CommandDispatcher.a("allow").executes((commandcontext) -> { return execute(commandcontext.getSource(), ArgumentEntity.f(commandcontext, "target")); }))); dispatcher.register(literalArgumentBuilder); } private static int execute(CommandListenerWrapper commandListenerWrapper, Collection<EntityPlayer> players) { for(EntityPlayer entityPlayer : players) { entityPlayer.abilities.canFly = true; entityPlayer.updateAbilities(); } if(players.size() == 1) { commandListenerWrapper.sendMessage(new ChatMessage("%s を飛行可能にしました", new Object[] {players.iterator().next().getScoreboardDisplayName()}), true); }else { commandListenerWrapper.sendMessage(new ChatMessage("%s 体のプレイヤーを飛行可能にしました", new Object[] {players.size()}), true); } return players.size(); } }public static void register(CommandDispatcher<CommandListenerWrapper> dispatcher) {・コマンド登録用のメソッドを作ります。
・CommandDispatcher
はcom.mojang.brigadier.CommandDispatcher
の方ですLiteralArgumentBuilder<CommandListenerWrapper> literalArgumentBuilder = net.minecraft.server.v1_16_R1.CommandDispatcher.a("fly").requires((permission) -> { return permission.hasPermission(2); }).then(net.minecraft.server.v1_16_R1.CommandDispatcher.a("target", ArgumentEntity.d()).then(net.minecraft.server.v1_16_R1.CommandDispatcher.a("allow").executes((commandcontext) -> { return execute(commandcontext.getSource(), ArgumentEntity.f(commandcontext, "target")); })));・
net.minecraft.server.v1_16_R1.CommandDispatcher.a("fly")
/flyの部分です。・
requires((permission) -> {
return permission.hasPermission(2);
});
コマンドを実行できる権限レベルを指定しています。2
を指定すると管理者権限を持つプレイヤーのみ実行できます。・
then(net.minecraft.server.v1_16_R1.CommandDispatcher.a("target", ArgumentEntity.d()).then(net.minecraft.server.v1_16_R1.CommandDispatcher.a("allow"))
/flyの後の構文です
ArgumentEntity.d()
はプレイヤーのみを指定させたい時に使います。もし、@eなどエンティティを指定した場合このようにエラーが出るようになります。executes((commandcontext) -> { return execute(commandcontext.getSource(), ArgumentEntity.f(commandcontext, "target")); })・/fly <プレイヤー> allowが実行された時の処理を記述します。
・getSource()
でCommandListenerWrapper
を取得できます。
・ArgumentEntity.f(commandcontext, "target")
でCollection<EntityPlayer>
を取得できます。dispatcher.register(literalArgumentBuilder);・コマンドの構文を登録しています。
for(EntityPlayer entityPlayer : players) { entityPlayer.abilities.canFly = true; entityPlayer.updateAbilities(); }・
entityPlayer.abilities.canFly = true
で飛行可能にできます。
・entityPlayer.updateAbilities()
はサーバーにプレイヤーの能力(entityPlayer.abilities)
に変更があったことを送信します。変更した際は必ずこれを記述しましょう。if(players.size() == 1) { commandListenerWrapper.sendMessage(new ChatMessage("%s を飛行可能にしました", new Object[] {players.iterator().next().getScoreboardDisplayName()}), true); }else { commandListenerWrapper.sendMessage(new ChatMessage("%s 体のプレイヤーを飛行可能にしました", new Object[] {players.size()}), true); }・
commandListenerWrapper.sendMessage()
はコマンド送信者にメッセージを送信します。
・getScoreboardDisplayName()
を使うとこのようにカーソルを合わせた際に情報が表示されるようになります。
・true
を指定すると他のプレイヤーや、サーバーログなどにも送信されます。
・false
を指定するとコマンド送信者のみ送信されます。Main.javapackage com.hamusuke.qiita; import org.bukkit.Bukkit; import org.bukkit.craftbukkit.v1_16_R1.CraftServer; import org.bukkit.plugin.java.JavaPlugin; import com.hamusuke.qiita.command.FlyCommand; import com.mojang.brigadier.CommandDispatcher; import net.minecraft.server.v1_16_R1.CommandListenerWrapper; import net.minecraft.server.v1_16_R1.DedicatedServer; public class Main extends JavaPlugin { @Override public void onEnable() { DedicatedServer dedicatedServer = ((CraftServer)Bukkit.getServer()).getServer(); CommandDispatcher<CommandListenerWrapper> dispatcher = dedicatedServer.vanillaCommandDispatcher.a(); FlyCommand.register(dispatcher); getLogger().info("registered fly command"); } }・プラグインがロードされた時にonEnable()が呼び出されるので、ここでコマンドを登録します。
サーバーに入れてみる
バニラコマンドとして登録されているので、
/minecraft:fly
になります。(minecraft:は省略可能)良いところ
・バニラコマンドと同じように機能する。
・plugin.ymlに記述しなくても良い。
・タブ補完用のクラスを作らなくても良い悪いところ
・コードが長くなりがちである。
終わりに
記事を見ていただきありがとうございます。
記事を書いたのは初めてなので、分かりにくいところがあったかも知れません。
- 投稿日:2020-07-11T20:33:09+09:00
enumの比較は==がつよつよ、equalsはよわよわ【Java】
理由
- ==はNullPointerExceptionが発生しない/ equalsは発生する
- ==は型変換がないため無駄がない(速い)/ equalsはObject型への型変換があるため無駄がある(遅い)
- ==はコンパイル時に型チェックがある/ equalsは型チェックない
==はNullPointerExceptionが発生しない/ equalsは発生する
ぬるぽ確認public class EnumNullPointerExceptionTest { enum Season { SPRING, SUMMER, AUTUMN, WINTER; } public static void main(String[] args) { Season season = null; if (season != Season.SPRING) { System.out.println("季節は春ではありません。"); } if (!season.equals(Season.SPRING)) { System.out.println("季節は春ではありません。"); } } }実行結果季節は春ではありません。 Exception in thread "main" java.lang.NullPointerException at EnumNullPointerExceptionTest.main(EnumNullPointerExceptionTest.java:13)==は通って、equlasでぬるぽで怒られた(実際に通ったのは、!=だけども)。
NullPointerExceptionが発生しない==つよつよ
NullPointerExceptionが発生しちゃうequlasよわよわ==は型変換がないため無駄がない(速い)/ equalsはObject型への型変換があるため無駄がある(遅い)
==の処理時間計測public class EnumSpeedTest1 { enum Season { SPRING, SUMMER, AUTUMN, WINTER; } public static void main(String[] args) { Season season = Season.SPRING; long startTimeMs = System.currentTimeMillis(); for (int i = 0; i < 2000000000; i++) { if (season == Season.SPRING) {} } long executionTimeMs = System.currentTimeMillis() - startTimeMs; System.out.println("比較を20億回繰り返すのにかかった時間は" + executionTimeMs + "ミリ秒です"); } }==の実行結果比較を20億回繰り返すのにかかった時間は2ミリ秒ですequlasの処理時間計測public class EnumSpeedTest2 { enum Season { SPRING, SUMMER, AUTUMN, WINTER; } public static void main(String[] args) { Season season = Season.SPRING; long startTimeMs = System.currentTimeMillis(); for (int i = 0; i < 2000000000; i++) { if (season.equals(Season.SPRING)) {} } long executionTimeMs = System.currentTimeMillis() - startTimeMs; System.out.println("比較を20億回繰り返すのにかかった時間は" + executionTimeMs + "ミリ秒です"); } }equalsの実行結果比較を20億回繰り返すのにかかった時間は4ミリ秒ですわずかだけど、==の方は型変換しないから速いね。
型変換がなくて無駄がない==つよつよ
型変換が必要で無駄があるequalsよわよわ==はコンパイル時に型チェックがある/ equalsは型チェックない
コンパイル確認public class EnumCompileTest { enum Season { SPRING, SUMMER, AUTUMN, WINTER; } enum Color { RED, GREEN, BLUE; } enum Animal { DOG, CAT; } public static void main(String[] args) { if (Season.SPRING.equals(Color.RED)) {} if (Season.SPRING == Animal.CAT) {} } }コンパイル結果$ javac EnumCompileTest.java EnumCompileTest.java:10: エラー: 型SeasonとAnimalは比較できません if (Season.SPRING == Animal.CAT) {} ^ エラー1個型が違う==の比較は怒られた。
コンパイル時に型チェックがある==つよつよ
コンパイル時に型チェックないequalsよわよわ番外:なぜenumは==で比較できるのか?
言語仕様には、こう書いてあるよ。
8.9.1. Enum ConstantsBecause there is only one instance of each enum constant, it is permitted to use the == operator in place of the equals method when comparing two object references if it is known that at least one of them refers to an enum constant.
there is only one instance of each enum constantってことは、
enumのインスタンスはシングルトンなんだね。だから、==で比較できると。参考
- 投稿日:2020-07-11T16:45:49+09:00
Java StringBuilderクラスについて
StringBuilderクラス、の『クラス』とは、Javaに標準装備されている機能のこと、という理解で正しいでしょうか?
また、「プログラムを実行するための処理をまとめたオブジェクト」、という意味のクラスとは別の用語なのか否かも教えて欲しいです。
拙い投稿申し訳ありません。
- 投稿日:2020-07-11T15:51:31+09:00
cask+anyenv+jenvを使ったJavaのバージョン管理
環境
OS:macOS Catalina 10.15.5
brew:2.4.3caskを使ったJavaのインストール
brew caskをそのまま使うと古いバージョンのJavaをインストールができない。
そのため以下のコマンドでを使って古いバージョンもインストールできるようにする。$ brew tap homebrew/cask-versions
各種バージョンをインストールする
// インストール可能なjavaのバージョンの確認 $ brew search java // java14 $ brew cask install java // java11 $ brew cask install java11 // java8 $ brew cask install adoptopenjdk8 // javaのバージョン確認 $ /usr/libexec/java_home -Vanyenvのインストール
anyenvは*envを管理することができる。今回の場合はjenvを管理するために導入する。
// brewでanyenvをインストール $ brew install anyenv // パス追加 zsh版 $ echo 'eval "$(anyenv init -)"' >> ~/.zshrc // マニフェストディレクトリ作成 $ anyenv install --init これで.anyenvディレクトリが作成される。 // SHELLの再起動 $ exec $SHELL -l // anyenvコマンド // インストールした*envのバージョン確認 $ anyenv versions // anyenvでインストールできる*envのリスト $ anyenv install -l※うまいことzshrcの設定が反映されない場合は
source ~/.zshrc
で反映する便利なanyenvのプログインを入れる
プラグインは任意ですが入れておくと便利です。
// プラグイン用のディレクトリ作成 $ mkdir -p ~/.anyenv/plugins // anyenv-update anyenvで入れた*envを一括でアップデートしてくれるプラグイン $ git clone https://github.com/znz/anyenv-update.git ~/.anyenv/plugins/anyenv-update // anyenv-git anyenvで入れた*envをgitコマンドで実行できるプラグイン $ git clone https://github.com/znz/anyenv-git.git ~/.anyenv/plugins/anyenv-gitjenvのインストール
caskでインストールしたJavaを管理する
// jenvをインストール $ anyenv install jenv // SHELLの再起動 $ exec $SHELL -l // 確認 $ anyenv versions jenv: system 1.8 1.8.0.252 11 11.0 11.0.2 14 14.0 * 14.0.1 (set by /Users/masa/.anyenv/envs/jenv/version) openjdk64-1.8.0.252 openjdk64-11.0.2 openjdk64-14.0.1Javaをjenvで管理する設定
各種Javaのバージョンをjenvに登録して切り替える方法です。
// 紐付け jenvにaddする $ jenv add `/usr/libexec/java_home -v "1.8"` $ jenv add `/usr/libexec/java_home -v "11"` $ jenv add `/usr/libexec/java_home -v "14"` // jenvで管理しているjavaのバージョン確認 $ jenv versions // global設定 $ jenv global 11.0.2 // javaのバージョン確認 $ java versions // シェル起動 $ jenv exec zsh // JAVA_HOMEを自動で設定するようにする $ jenv enable-plugin export // JAVA_HOME確認 $ echo $JAVA_HOME // Topic 特定のディレクトリ内で別バージョンのJDKを利用する場合 $ jenv local 11.0.2※JAVA_HOMEの設定がうまいこと反映されない場合はterminalを再起動等すれば反映されるときがあります。
- 投稿日:2020-07-11T12:38:24+09:00
[servlet][JSP][tomcat]について
はじめに
現在、servlet/JSP/tomcatについてpaizaで勉強中です。
アウトプットの一環としてキータを書いていきます。servletとは
WEBサーバー上で動く、JAVAで書かれたプログラムのことである。
WEBブラウザからのリクエストに応えて、処理を行うのが仕事である。JSPとは
servletは内部の処理をしていたが、JSPは画面に文字を表示させる仕事をする。
HTMLの中にJAVAを埋め込み動的にWEBを表示させる。
railsで言うところのerbファイルである。tomcatとは
servletやJSPを動かすときに使うサーブレットコンテナのことである。
HTTPリクエストが来たらservletに命令して、動くようにしている。servlet/JSPの基本的な書き方
Javaファイルの作成
tomcat/webapps/該当ファイル名の中にHelloWorld.javaのようなファイルを作成。
import java.io.*; import javax.servlet.*; import javax.servlet.http.*; public class HelloWorld extends HttpServlet { public void doGet(HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException { // 後ほど作成するJSPファイルを読み込めるように設定 String view = "/WEB-INF/views/index.jsp"; RequestDispatcher dispatcher = request.getRequestDispatcher(view); dispatcher.forward(request, response); } }コンパイルする
書き終わったらコンパイルする。
> cd tomcat/webapps/該当ファイル名 > javac -classpath "../../lib/servlet-api.jar" -d WEB-INF/classes HelloWorld.javatomcatの再起動を不要にする
tomcat/conf/Catarina/localhostにmywork.xmlを作成して、以下を記述。
<Context reloadable="true"/>jspファイルを作成する
tomcat/webapps/該当ファイル名/WEB-INFにviewsディレクトリを作成する。
そしてその中にindex.jspを作成。<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%> <!DOCTYPE html> <html> <head> <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"> <title>Hello World</title> </head> <body> <h1>Hello World</h1> </body> </html>ルーティングを指定する。
tomcat/webapps/該当ファイル名/WEB-INFの中にweb.xmlを作成。
<?xml version="1.0" encoding="UTF-8"?> <web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd" version="4.0" metadata-complete="true"> <servlet> <servlet-name>Hello</servlet-name> <servlet-class>HelloWorld</servlet-class> //先ほど作成したjavaファイルのclass名 </servlet> <servlet-mapping> <servlet-name>Hello</servlet-name> <url-pattern>/hello</url-pattern> //パスを記述 </servlet-mapping> </web-app>これで該当ファイル/helloと言うurlでアクセスするとHello Worldが表示される。
JSPのタグ
JSPにJAVAを埋め込むときに使用するタグの種類は下記の通りです。
railsで言うところの<% %>の部分です。// コードの実行 <% %> // コードを実行し、結果を出力 <%= %> // JSPの宣言(index.jspの先頭に使った) <%@ %> // メソッドや変数の宣言 <%! %> // コメント <%- -%>servletからJSPにデータを送り、表示させる。
servletに
import java.io.*; import javax.servlet.*; import javax.servlet.http.*; public class HelloWorld extends HttpServlet { public void doGet(HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException { // requestは上のHttpServletRequestのこと。 // JSPでmessageを使うとHello worldが表示される。setAttributeなので注意。 request.setAttribute("message", "Hello world"); } }JSP側は
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%> <!DOCTYPE html> <html> <head> <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"> <title>Hello Template</title> </head> <body> // requestオブジェクトから先ほどのmessageを取り出す。こちらはgetAttributeなので注意。 // String型にキャストして、String型のmesageに代入して、したで表示。 <% String message = (String)request.getAttribute("message");%> <p><%= message %></p> </body> </html>終わりに
簡単でしたが、まとめました!
- 投稿日:2020-07-11T01:27:39+09:00
Javaのメソッド
はじめに
前回の配列の投稿でスパム認定されましたこんにちは。
メソッド
・メソッドとは部品だったりゲームの技ようなもの(クラスはステータスのような※あくまで私の感覚)。
・クラスの{}(ブロック)の中に書かないと動かない。
・メソッドを使うことによって同じ処理などする時なんども同じ処理を書かずに呼ぶだけで済むので楽。
・コードの見通しや管理が楽になる。kane.javapublic static void メソッド名() { 実行する処理 ; }kane.javaclass Main{ public static void main(String[]args) { banana() ; //()は必要。 } public static void banana() { System.out.println("黄色") ; } }・戻り値とは
呼び出されたメソッドから、呼び出し元のメソッドへ返す値(データ)のことを戻り値または返り値という。public static void main(String[] args) { }
・javaのプログラムを実行した場合自動でmainメソッドから始まる。。
・main メソッドが各メソッドに指示を出し、支持された各メソッドが個々の処理を実行する。・忘れず書いとけば今はおK。
引数
・メソッドに与える追加情報のようなもの。
・メソッドを呼び出す際に呼び出し元から値を渡すことができる。
・それによって渡されたメソッド内でもその値が使えるようになる引数を受け取れるメソッドを定義するために、メソッドの定義部分で、引数を受け取るための箱となる変数(仮引数(かりひきすう))を指定し public static void banana() の()に仮引数を指定する。
仮引数は、変数定義と同様に、データ型を指定する必要があります。kane.javaclass Main{ public static void main(String[]args) { banana("赤") ; } public static void banana(String color) { //データ型を指定 System.out.println("このバナナの色は" + color + "です。") ; } }何も引き渡す情報がなかったとしても ( ) は必要なので必ず記入する。
・複数の引数を渡す場合
kane.javaclass Main{ public static void main(String[]args) { banana("フィリピン", 5) ; } public static void banana(String origin, int year) { System.out.println("このバナナは" + origin + "産の" + year + "年ものです。") ; } } 実行結果 このバナナはフィリピン産の5年ものです。//それは腐ってます戻り値
オーバーロード
- 投稿日:2020-07-11T01:13:18+09:00
言語別Stack処理速度比較
1. はじめに
Go言語にはStackが実装されていませんが、
Stackの処理自体は、どの言語でも、
配列や線形リストを使って自作で実装することができます。Go言語向けに自作で作ったついでに、
Stackが実装されている他言語との処理速度(処理時間)を比較してみました。実験で使用した言語は、
- C#(.NET Core3.0)
- go1.14.4
- java 13.0.2
- Python 3.8.3
- C言語(MinGW.org GCC-8.2.0-3)(2020/07/12追記)
の5言語です。
各言語のソースは、githubでも公開しています。
https://github.com/NobuyukiInoue/StackSpeedTest
C#
https://github.com/NobuyukiInoue/StackSpeedTest/tree/master/Project_CSGo(配列でStackを実装)
https://github.com/NobuyukiInoue/StackSpeedTest/tree/master/Project_Go_Stack_by_ArrayGo(線形リストでStackを実装)
https://github.com/NobuyukiInoue/StackSpeedTest/tree/master/Project_Go_Stack_by_ListNodeJava
https://github.com/NobuyukiInoue/StackSpeedTest/tree/master/Project_Java_Stack
https://github.com/NobuyukiInoue/StackSpeedTest/tree/master/Project_Java_ArrayDeque
https://github.com/NobuyukiInoue/StackSpeedTest/tree/master/Project_Java_DequePython
https://github.com/NobuyukiInoue/StackSpeedTest/tree/master/Project_Python3C言語(64K個のセグメント(配列)でStackを実装)
https://github.com/NobuyukiInoue/StackSpeedTest/tree/master/Project_C_Stack_by_SegmentC言語(線形リストでStackを実装)
https://github.com/NobuyukiInoue/StackSpeedTest/tree/master/Project_C_Stack_by_ListNode2. 言語別のメソッド比較
Stackといっても、言語によって、各種操作がメソッドで提供されていたり、
プロパティでチェックしなければならないなど、細かな処理の実装内容が異なります。今回の実験で使用した各言語でのクラス/インタフェース/機能を一覧にすると、以下のようになります。
処理内容 C#(.NET Core3.x)
(Stackクラス)Java 8
(クラスStack)Java 8
(クラスArrayDeque)Java 8
(クラスDeque)Python3.8.3
(リスト)Stackへの書き込み Pushメソッド pushメソッド pushメソッド addFirstメソッド append関数 Stackからの取り出し Popメソッド popメソッド popメソッド removeFirstメソッド pop関数 値の検索 Containsメソッド
(戻り値はbool型)searchメソッド
(戻り値はindex番号)Containsメソッド
(戻り値はbool型)Containsメソッド
(戻り値はbool型)index関数 Stackが空かどうか調べる Countプロパティを利用 emptyメソッド isEmptyメソッド isEmptyメソッド len関数を利用 3. 検証環境と実験結果(2020/07/11追記)
項目 値 OS MS-Windows 10 CPU Intel Core-i7 8559u メモリ 16GB(LPDDR3/2133MHz) メモリ容量を追記しました。(2020/07/11)
処理の内容は、
- 1~1億までの数値のpush()
- 最も深い位置にある値1の検索
- 1億~1までのPop()
です。
1億個の数値をint64(8byte)で割り当てると、762MByteの容量が必要になります。
int32(4byte)なら、半分の381MByteですね。生成するスタックの容量が小さいうちは、それほど処理時間に差はありませんが、
1億個のスタック生成となると、選択した方法によってはメモリ消費が4GBを超えます。搭載されているメモリが8GB以下だと、言語によっては(デフォルト設定では)、
ヒープメモリが足りないといったエラーがでます。各言語での処理時間は以下の通りでした。
消費したメモリについても、Windows10のリソースモニター(コミット値)による表示の目視ではありますが、参考程度に掲載しています。(2020/07/11追記)
C# NET Core 3.0.100
Stackクラスを使用
https://docs.microsoft.com/ja-jp/dotnet/api/system.collections.generic.stack-1?view=netcore-3.1メモリ消費は0.39GBでした。
times Execution time 1st 977 [ms] 2nd 1003 [ms] 3rd 1004 [ms] メモリ消費からみて、内部ではint32で処理が行われているようです。
他言語と比べてかなりメモリ消費が少なく、
処理データの少なさが処理時間の短さに影響しているようです。今回の比較では最速の数値です。
go1.14.4 windows/amd64(配列版)
メモリ消費は2.36GBでした。
times Execution time 1st 2089 [ms] 2nd 1853 [ms] 3rd 1737 [ms] 配列の再割り当てを繰り返しても、それほど遅くはないようです。
Javaより速い結果となりました。go1.14.4 windows/amd64(線形リスト版)
メモリ消費は1.49GBでした。
times Execution time 1st 5617 [ms] 2nd 5400 [ms] 3rd 5569 [ms] 処理時間は配列版よりも遅くなりました。
線形リストでは次ノードへのポインタが必要なので、
直線的なメモリ配置の配列より不要なデータが増えてしまいますが、
配列版より消費が少ない結果となりました。Go言語では、配列の再割り当てを行っても、
プログラムの終了までメモリの解放が行われていないのかもしれません。
(あくまで推測で、だれか検証してほしい)java 13.0.2(クラスStack版)
クラスStackを使用
https://docs.oracle.com/javase/jp/8/docs/api/java/util/Stack.htmlメモリ消費は4.21GBでした。
times Execution time 1st 6743 [ms] 2nd 6690 [ms] 3rd 6733 [ms] java 13.0.2(クラスArrayDeque版)
クラスArrayDequeを使用
https://docs.oracle.com/javase/jp/8/docs/api/java/util/ArrayDeque.htmlメモリ消費は3.96GBでした。
times Execution time 1st 4397 [ms] 2nd 4612 [ms] 3rd 4477 [ms] クラスStackよりも処理時間が短くなりました。
java 13.0.2(インタフェースDeque版)
インタフェースDequeを使用
https://docs.oracle.com/javase/jp/8/docs/api/java/util/Deque.htmlメモリ消費は4.31GBでした。
times Execution time 1st 21416 [ms] 2nd 20187 [ms] 2nd 20341 [ms] クラスStackよりも処理時間が増えてしまいました。
Python 3.8.3
リストを使用
メモリ消費は4.42GBでした。
times Execution time 1st 16957 [ms] 2nd 16561 [ms] 3rd 17558 [ms] 他言語では、一度確保したメモリは消費したままでしたが、
Python3では、1億個のPush直後がメモリ消費の最大値で、
Pop処理が進むにしたがって消費メモリが減っていく様子が目視できます。C言語(64K個単位のセグメント(配列)でStackを実装)(2020/07/12追記)
メモリ消費は0.38GBでした。
さすがにC言語は速いですね。
といっても、C#に負けてしまいました。確保したブロックは破棄せずにそのまま保持するようにすれば、もっと速くなるかもしれません。
times Execution time 1st 1591 [ms] 2nd 1554 [ms] 3rd 1558 [ms] C言語(線形リストでStackを実装)(2020/07/12追記)
メモリ消費は1.55GBでした。
Push処理、Pop処理、それぞれ5秒程度ずつといったところでしょうか。
メモリの確保・解放の処理回数が多いので、それだけ処理時間が遅くなっています。
times Execution time 1st 10981 [ms] 2nd 11066 [ms] 3rd 10799 [ms] 関連(2020/07/11追記)
LeetCodeのProblemの中にも、Stackを自分で実装する問題があります。
いろんな言語での回答例が投稿されているので、処理時間を比較するのも面白いと思います。
- 225. Implement Stack using Queues
https://leetcode.com/problems/implement-stack-using-queues/