【Kotlin】APIを叩く【Java】

KotlinでAPIを叩いてJSONを取得します。
APIを叩くことを勉強しはじめたばかりなので分かってないことばかりですが、URLを叩くとJSONが帰ってくる状況を想定して作りました。

ソースコード

GitHubAPIを叩いて、自分のアカウント(k163377)の全リポジトリの一覧を取得しています。
GitHubAPIに関しては公式にライブラリが有るようなのですが、とりあえず今回は使いません。

import java.io.BufferedReader
import java.io.InputStreamReader
import java.net.URL

fun getJsonFromURL(url: URL): String {
    var resultJson = ""
    var br = BufferedReader(InputStreamReader( url.openStream() ))

    for(line in br.lines()) resultJson += line

    return resultJson
}

fun main(args:Array<String>) {
    println(getJsonFromURL(URL("https://api.github.com/users/k163377/repos")))
}

参考ページ

ほぼ以下のページのコピペです。
bigbuddha.hatenablog.jp

【日記】プロジェクトをクラスライブラリに分割する中で後悔したこと

二度と繰り返したくないので言語化します。

背景

ソースコードを流用して別プロジェクトを作ろうとする中で、複数のプロジェクトから同じコードを使いたいので、機能のクラスライブラリへの分割を行っています。

内容はVB.NetWindowsフォームアプリケーションで、諸々のコンポーネントや機能をクラスライブラリに分割して種々の機能を作り直せば簡単にできるだろうと考えていました。

元のコード

元のコードは自分がフルスクラッチでリライトしたものです。つまりこの件で悪いのは全部自分です。以下の記事でリライトしていた*1コードです。

wrongwrong163377.hatenablog.com

起きた問題

起きた問題は大きく以下の2点です

  • 巨大な定数クラスへの依存による分割の困難さの発生
  • マズい変数の渡し方と各機能の利用
巨大な定数クラスへの依存

巨大な定数クラスはとても便利でした。実装当時は.NetのPropertieやらDictionaryやらを使ってスマートに仕上げた気でいました。が、実際に機能のクラスライブラリへの分割を行ってみると、この定数部が恐ろしい勢いで足を引っ張りました。

機能ごとの制御に定数クラスの変数を利用してしまったせいで分割は恐ろしく煩雑で大規模な変更を伴いました。二度とやりません。

定数クラスを作るとしても全てを1つに纏めるようなことはせず、機能ごとに管理する定数をしっかり割り振った上で作るようにしようと思います。

定数クラス自体は「よく使う定数を引数でリレーするような形にならない」という利点があるので、決して害悪とは言えないと思っています。できるだけスマートに利用していこうと思います。

マズい変数の渡し方と各機能の利用

「末端の機能にはリテラルで渡せ」というのが今回最大の教訓です。というのも「定数クラスの値で渡して、諸々の処理にそれを流用する」ということをやってしまったせいで余計な依存が生まれ、クラスライブラリへの分割で物凄く苦労しているからです。

「後から見返してそうだった」というのが大きいとは思いますが、ファイルパスを渡せば済む所で無駄に定数クラスへの依存を作ってしまったせいで分割が上手く行かなくなったり、「定数クラスへ依存しないように」と作ったはずの関数で依存を発生させていた時は本当に辛かったです。

残りの分割に掛ける労力を考えると頭が痛いというか、生産的じゃない作業過ぎて死にたくなります。辛い。

反省

巨大な定数クラスを作らないことと、設計をしっかり考え、できるだけリテラルで引数をやり取りすることを心がけようと思っています。

極端な言い方になりますが、最初からクラスライブラリへの分割をやっていれば依存を発生させないコーディングができたんじゃないかとすら思っています。

 割と『秘伝のタレ、製造中』みたいな感じになりつつあるので、できるだけスマートなコードにまとめられるように頑張ろうと思います(というか、あんだけDisっておいてちゃんとしたコードを残せないってのは……)。特に割とお金を貰っているという点は大きいですね。がんばります。

*1:あれから半年ってマジか!?

【Kotlin】mXparserを使う【Java】

mXparserを使ってみました。

mXparserとは

Stringで諸々を入力してやることで数式を計算できるライブラリです。Javaや.Net、AndroidやXamarinといった多くの環境で動きます。Javaで動くので当然Kotlinでも動きます。
機能も充実しており、大体の計算はできます。
詳しくは公式ページを参照してください。

やったこと

練習がてら以下の画像*1の例をKotlinに写経しました。
http://mathparser.org/wp-content/uploads/2017/05/mxparser-demo-r2.gif

ソースコード
import org.mariuszgromada.math.mxparser.*;

fun main(args:Array<String>) {
    /*
     * 三角形の面積
     */
    val At = Function("At(b, h) = 1/2 * b * h") //関数を設定
    val e1 = Expression("At(2, 4)", At) //実行内容を設定
    mXparser.consolePrintln(e1.expressionString + " = " + e1.calculate()) //実行と出力

    var b = Argument("b = 3") //引数を設定
    var h = Argument("h = 10")
    var e2 = Expression("At(b, h)", At, b, h) //引数をセット
    mXparser.consolePrint(e2.expressionString + " = " + e2.calculate()) //実行と出力
}
実行結果
[mXparser-v.4.1.1] At(2, 4) = 4.0
[mXparser-v.4.1.1] At(b, h) = 15.0

導入方法

Intelij IDEAでの導入方法です。というか.jarの導入方法です。
ダウンロードページから.zipを落としてきて、中身をどこかに解凍します。
f:id:wrongwrongwrongwrong163377:20180710144735p:plain
解凍したディレクトリから[path to dir]MathParser.org-mXparser-[version]/bin-only/jdk.1.8/MathParser.org-mXparser-jdk18-[version].jarを探し、以下の手順で導入できます。
stackoverflow.com

*1:公式ページJAVA Demoより引用。

【c/c++】Microsoft PPL/並列STL(C++17)/OpenMPを少しだけ比較【C++17】

Visual Studioc++で利用できる並列化手法としては、Microsoft PPL、並列STL(C++17)、OpenMPなどが有ります。この内どれが速いのが知りたくなったので実行速度を比較しました。
手法ごとに使う機能が異なっている(後述)ため、比較としてはかなりよろしくない部分がありますが、まあ大体の傾向は出たかな?という感じです。
CPUはi7 6700(8スレッド)で検証を行いました。

比較に用いた問題

モンテカルロ法による円周率の導出です。
問題の性質として、ループ内での処理の量は均等です。

実装で利用したもの

各並列化手法の実装では「その手法の中で完結すること」に重点を置きました。
例えば結果の集計方法では、OpenMPのreductionやPPLのcombinableなどがありますが、OpenMPではOpenMPの機能を、PPLではPPLの機能を用いて実装を行いました。
そのためどの機能が実行速度に影響しているのか上手く絞りきれていません。というかどの手法もそれぞれに詰めていくと際限が無い*1のでゆるふわメモとして残します。

各種法ごとに利用したもの

Microsoft PPL

concurrency::parallel_for_eachで並列化しました。結果の集計はconcurrency::combinableで行いました。

並列STL

algorithmのstd::for_eachを並列化しました。結果の集計はstd::atomicで行いました

OpenMP

#pragma omp parallel forで並列化しました。結果の集計は#pragma omp atomicで行いました。

コード

#include <chrono> //時間計測その他諸々
#include <cstdio>
#include <iostream>
#include <random>

#include <ppl.h> //parallel_for_each用

#include <algorithm> //for_each用
#include <execution> //algorithmの並列実行ポリシー

#include <omp.h> //OpenMP用

#define num 1000000000
#define threadnum 8;

inline double sqr(double d) { return d * d; }
inline bool check(double x, double y) { return (sqr(x) + sqr(y) > 1.0); }

double Single() { //単一スレッド
	using namespace std;

	mt19937 m = mt19937((int)time(NULL));
	constexpr int tnum = threadnum;
	mt19937_64 mts[tnum];
	for (mt19937_64 &mt : mts) { mt = mt19937_64(m()); }
	uniform_real_distribution<> uniform = uniform_real_distribution<>(0.0, 1.0);

	constexpr long L = num / tnum;
	long count = 0;
	for (int i = 0; i < tnum; i++) {
		for (long j = 0; j < L; j++) {
			if (check(uniform(mts[i]), uniform(mts[i]))) { count++; }
		}
	}

	return (double)(num - count) / (double)(num >> 2);
}

double PPL() { //concurrency::parallel_for_each
	using namespace std;

	mt19937 m = mt19937((int)time(NULL));
	constexpr int tnum = threadnum;
	mt19937_64 mts[tnum];
	for (mt19937_64 &mt : mts) { mt = mt19937_64(m()); }
	uniform_real_distribution<> uniform = uniform_real_distribution<>(0.0, 1.0);

	constexpr long L = num / threadnum;
	concurrency::combinable<long> count;
	concurrency::parallel_for_each(begin(mts), end(mts), [&count, uniform, L](mt19937_64 mt) {
	//concurrency::parallel_for(0, tnum, [&count, &mts, uniform, L](int i) { //こっちのが4~5秒ぐらい遅い
		long c = 0;
		for (long j = 0; j < L; j++) {
			if (check(uniform(mt), uniform(mt))) { c++; }
		}
		count.local() += c;
	});

	return ((double)(num - count.combine(std::plus<long>())) / (double)(num >> 2));
}

double STL() { //c++17の並列for_each
	using namespace std;

	mt19937 m = mt19937((int)time(NULL));
	constexpr int tnum = threadnum;
	mt19937_64 mts[tnum];
	for (mt19937_64 &mt : mts) { mt = mt19937_64(m()); }
	uniform_real_distribution<> uniform = uniform_real_distribution<>(0.0, 1.0);

	constexpr long L = num / threadnum;
	atomic_long count = 0;
	for_each(execution::par_unseq, begin(mts), end(mts), [&count, uniform, L](mt19937_64 mt) {
		long c = 0;
		for (long j = 0; j < L; j++) {
			if (check(uniform(mt), uniform(mt))) { c++; }
		}
		count += c;
	});

	return ((double)(num - count) / (double)(num >> 2));
}

double OMP() { //OpenMP
	using namespace std;

	mt19937 m = mt19937((int)time(NULL));
	constexpr int tnum = threadnum;
	mt19937_64 mts[tnum];
	for (mt19937_64 &mt : mts) { mt = mt19937_64(m()); }
	uniform_real_distribution<> uniform = uniform_real_distribution<>(0.0, 1.0);

	constexpr long L = num / tnum;
	long count = 0;

	#pragma omp parallel for private(uniform)
	for (int i = 0; i < tnum; i++) {
		long c = 0;
		for (long j = 0; j < L; j++) {
			if (check(uniform(mts[i]), uniform(mts[i]))) {
				c++;
			}
		}
		#pragma omp atomic
		count += c;
	}

	return (double)(num - count) / (double)(num >> 2);
}

int main() {
	using namespace std;

	clock_t s, e;
	double pi;

	s = clock();
	pi = Single();
	e = clock();
	printf("StNs\t: %f sec.\tpi = %f\n", (float)(e - s) / CLOCKS_PER_SEC, pi);

	s = clock();
	pi = PPL();
	e = clock();
	printf("PPL\t: %f sec.\tpi = %f\n", (float)(e - s) / CLOCKS_PER_SEC, pi);

	s = clock();
	pi = STL();
	e = clock();
	printf("STL\t: %f sec.\tpi = %f\n", (float)(e - s) / CLOCKS_PER_SEC, pi);

	s = clock();
	pi = OMP();
	e = clock();
	printf("OMP\t: %f sec.\tpi = %f\n", (float)(e - s) / CLOCKS_PER_SEC, pi);

	cin >> pi;

	return 0;
}

実行結果

何度か試してみましたが、傾向としてはPPLが最も高速でした。

StNs    : 57.617001 sec.        pi = 3.141573
PPL     : 10.830000 sec.        pi = 3.141636
STL     : 10.840000 sec.        pi = 3.141597
OMP     : 14.084000 sec.        pi = 3.141583

考察

PPL

parallel_forも試しましたが、OpenMPより若干速い程度でした。
今回は最速となりましたが、parallel_forを用いてループを分割せずに全部回す条件ではOpenMPより5秒程度遅くなりました。
この原因は、今回の問題ではループ内の計算量が均一でしたが、恐らくダイナミックに負荷を調整しようとした結果ではないかと思います。

STL

とくに無いです。

OpenMP

最遅となりました。インデックスを使うアクセス速度が足を引っ張っていると思われ、問題との相性が悪かった結果でしょう。

その他

前回の記事から引き継いでSIMD命令も入れて試しましたが、なんと全部の手法で使わない方が速いという結果になりました。
恐らくキャッシュが足りない辺りが足を引っ張っているものだと思います。

検証できてないこと

PPLの項で触れましたが、ループ内の負荷が均一でない場合どうなるかは見ることができていません。
今回はインデックスが必須な問題ではなかったためOpenMPが不利であったりもしています。
もうちょっと別の問題でも確認してみたいですね。

まとめ

各種法の利点と欠点をまとめます。

PPL

Windows環境では条件付きで最速。
インデックス有りでも十分速い。
機能的にも様々な並列コンテナーやオブジェクトなどが揃っているが、利用可能な環境が少ない。

STL

C++17が使えるのなら、一番何も考えずに使うことができ、高速性も十分である。
インデックスが必要なループの並列化をどうするかが悩みどころ。

OpenMP

今回は最遅となったが、問題によっては最適化のしやすさから十分速くなる可能性がある。
扱える環境の幅広さも魅力。

*1:チョチョイと変えたら「SIMD使った方が遅い」まで行った辺りで泣きそうになりました。

【c/c++】AVX2を使った円周率を求めるプログラム――修正

結果の加算周りが非効率なためこの記事に載せたプログラムは遅いです。
この部分を修正した場合、AVX2無しで動かしたほうが高速です。
wrongwrong163377.hatenablog.com



wrongwrong163377.hatenablog.com
上記記事で作ったモンテカルロ法で円周率を求めるプログラムの実装がマズかったので、マルチスレッドSIMD有り(MtWs)のみ作り直しました。

マズかったところ

前回記事を書いた後でkawa0810様による以下の2つの記事を見つけました。
kawa0810.hateblo.jp
kawa0810.hateblo.jp

SSE や AVX では条件分岐文の変わりに cmp 関数を用いて計算を行います.ただし,SSE 世代と AVX 世代では cmp 関数の使用方法が大きく変更されました.SSE 世代で a == b を計算したければ _mm_cmpeq_ps() 関数,a < b を計算したければ _mm_cmplt_ps() 関数など演算子ごとに専用の関数が用意されていましたが,AVX 世代では以下の関数で全ての条件演算子の計算を行います.

はい。この部分完全に勘違いしていて、AVX2ではcmpが無いもんだと思ってました。
この他にも一部気に入らない点があったので書き直しを行いました。

修正後
double MtWs_Kai() {
    using namespace std;

    mt19937 m = mt19937((int)time(NULL));
    constexpr int tnum = threadnum;
    mt19937_64 mts[tnum];
    for (mt19937_64 &mt : mts) { mt = mt19937_64(m()); }

    constexpr long L = (num/tnum) >> 2;
    double count = 0.0;
    const __m256d one = _mm256_set1_pd(1.0);

    #pragma omp parallel for 
    for (int i = 0; i < tnum; i++) {
        int t;
        double d[4];
        __m256d vx, vy, vnorm, vans;
        vans = _mm256_setzero_pd();
        double* ans = reinterpret_cast<double*>(&vans);
        uniform_real_distribution<> uniform = uniform_real_distribution<>(0.0, 1.0);

        for (long j = 0; j < L; j++) {
            //乱数で取って
            for (t = 0; t < 4; t++) d[t] = uniform(mts[i]);
            vx = _mm256_load_pd(d);
            for (t = 0; t < 4; t++) d[t] = uniform(mts[i]);
            vy = _mm256_load_pd(d);
            //ノルム
            vnorm = _mm256_add_pd(_mm256_mul_pd(vx, vx), _mm256_mul_pd(vy, vy));
            //比較してマスクを取り、これと1のandを取ることで外側を0にして、加算する
            vans = _mm256_add_pd(vans, _mm256_and_pd(_mm256_cmp_pd(vnorm, one, _CMP_LE_OS), one));
        }

        #pragma omp atomic
        count += ans[0] + ans[1] + ans[2] + ans[3];
    }
    return count / (double)(num >> 2);
}

改善した点

全体をループにするのではなく、外側と内側のループに分ける

このやり方では以下の3点の利点があります。

  • 各スレッドで変数を宣言できるため、コピーが無い分高速。
  • スレッド分割がどうなっているか把握しやすい。
  • 変数のスコープが限定されるのでコードが見やすい。
点の位置判定と総和をAVX2命令で計算

はじめに書いたとおり、kawa0810様の記事を参考にcmp命令が使えるので判定部を置き直しました。

書き直した後の実行時間

前回記事でいうX=9で最速14.5秒程度でした。1.5秒程度の高速化がサックリ出来てしまいました。
勉強不足ェ……。

【c/c++】AVX2を使ってみて少しだけ比較

結果の加算周りが非効率なためこの記事に載せたプログラムは遅いです。
この部分を修正した場合、AVX2無しで動かしたほうが高速です。
wrongwrong163377.hatenablog.com



記事中のAVX2命令を用いたプログラムはcmp命令を使っていないため低速な状態です。これを修正したものは以下。
wrongwrong163377.hatenablog.com


レポートが詰まった挙げ句AVX512命令に手を出し、Skylake-XとSkylakeを見間違えて「AVX512が動かないいい!*1」と発狂しながら時間を浪費していましたが僕は元気です。
AVX2を使ってみました。ついでにAVX2の有無&OpenMPによる並列化の有無で比較してみました。



基本的な使い方

本題に入る前にAVX命令の基本的な使い方をまとめます。

作成環境

Visual Studio 2017のCmakeプロジェクトで作成しました。

includeするもの

immintrin.hをincludeすればOKです。一応このヘッダを入れておけばSIMD命令全般が動かせます。
このヘッダの内容は参考文献を御覧ください。

Cmakeのオプション指定

OpenMPが混ざっててアレですが、AVX2を使うならフラグに/arch:AVX2を追加します。こちらも詳しくは参考文献を御覧ください。

cmake_minimum_required (VERSION 3.8)
find_package( OpenMP )
set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${OpenMP_CXX_FLAGS} /O2 /arch:AVX2")
add_executable (CMakeProject1 "CMakeProject1.cpp" "CMakeProject1.h")
基本的な処理の流れ

AVX2(に限らずSIMD命令全般)を使った処理の流れは以下の通りです。

  1. 変数に対して、load命令で配列から読み込むか、set命令で1つの値でクリアして初期化。
  2. 演算を行う。
  3. store命令で配列にデータを書き出す。

要するに、演算前に処理しやすいようにまとめる処理と処理したものを配列の形に戻す処理が挟まるということですね。
add、mul、subのような基礎演算とset、load、storeのようなメモリへの読み書きが一番大きいでしょうか。この辺を使えるようになれば、探せばほかの操作もできると思います。
ただ、命名規則や実装内容が結構不規則な点は非常に使いにくいです。ほぼ自分も覚えられていません。

作ったもの

円周率をシミュレートするプログラムを以下のバリエーションで作成しました。

  1. シングルスレッド・AVX2無し(StNs)
  2. マルチスレッド・AVX2無し(MtNs)
  3. シングルスレッド・AVX2無し(StWs)
  4. マルチスレッド・AVX2有り(MtWs)
関数の処理の流れ

要するに原点を中心にした(1, 0)から(0, 1)までの半径1の1/4の円と(1, 0)から(0, 1)を対角線に持つ正方形を用意し、正方形の範囲に乱数的に点を沢山打つと、面積の比から円周率が計算できるというアレです。

  1. 乱数で0以上1以下の点(x, y)を生成。
  2. 座標の原点からの距離を計算。
  3. 結果が1以上なら円の外としてカウント*2
  4. (総試行回数 - カウント) * 4 / 総試行回数で円周率を取得*3
ソースコード

長いので折りたたみます。

出力例

num = 10000000での出力結果の例です*4

StNs    : 0.695000 sec. pi = 3.141440
MtNs    : 0.174000 sec. pi = 3.141440
StWs    : 0.605000 sec. pi = 3.141740
MtWs    : 0.166000 sec. pi = 3.141740

比較

実行環境
CPU Core i7 6700
モリー 16GB(2133C13)
OS Windows10
コンパイラ VC++2017*5
ビルドオプション /O2 /arch:AVX2 (x64 Release)
実行結果

numを変化させながら( X=\log_{10}num)として計測した結果です*6

X = 7 StNs MtNs StWs MtWs
実行時間 0.695 0.174 0.605 0.166
/StNs[%] 100 25.0 87.1 23.9
X = 8 StNs MtNs StWs MtWs
実行時間 6.221 1.681 5.836 1.648
/StNs[%] 100 27.0 93.8 26.5
X = 9 StNs MtNs StWs MtWs
実行時間 61.813 16.362 58.599 16.172
/StNs[%] 100 26.5 94.8 26.2
考察

X > 7ではAVX2を使っても全然早くなりませんでした。
というか乱数生成部が思いっきり足を引っ張っているようで、AVX2によるシングルスレッドの高速化という意味ではX > 7で頭打ちのようです。選んだ内容がアカンかった……。
やってみてから言うのもなんですが、こうして見ると「GPU使ったほうが速そうね」感が非常に強いです。ここまで最適化する意義が正直、その……。次遊ぶならOpenCLですかね?

参考文献

koturn様によるSIMD組み込み関数のまとめ。コンパイラ毎のコンパイルオプションや命令などが分かりやすくまとまっている。 SIMDの組み込み関数のことはじめ - koturnの日記
Intelによるリファレンス。 Intel Intrinsics Guide
@tanakmura様によるAVX512命令を用いたシャッフルプログラムのサンプル。 vshufps - Qiita
@yohholy様によるx86SIMD Intrinsicとヘッダファイルの対応のまとめ。 x86/SIMD Intrinsicとヘッダファイル対応表 - Qiita

*1:IntelのメインストリームにAVX512が降りてくるのはCannon Lake以降になりそうです。

*2:外側の方が面積が狭いので、カウント加算処理が少なくなるため(特にブロックが減るマルチスレッドで)高速化できます。

*3:4倍を先にやってしまうとオーバーフローするので最後に回します。

*4:乱数周りの実装がマズく、シングルスレッドの実行時間が1秒以内に収まると乱数の種が一致してしまうため、piの計算結果が次のマルチスレッドの実行結果と被ります。

*5:バージョンが確認できず。2018/6/29時点で最新のものだったはず。

*6:X > 9ではunsigned longの最大値も超えるので実行しない。

【日記】サポーターズ エンジニア"1on1"面談会に参加した

サポーターズ エンジニア"1 on 1"面談会に参加してきましたので、振り返ってまとめます。

イベント概要

名前の通り、企業の方と1 on 1で面談できるイベントです。

スケジュールとしては、『各企業の自己紹介 → 1 on 1(または1 on 2)で各企業さんと面談x8 → 懇親会( → 面談した企業さんから採点が届く)』という流れでした*1

全体を通しての感想

とにかく自己紹介を何度もするというのがキツかったですが、それ以上に面談を通して得られたモノが大きかったです。

特に自分が働く時に取るであろうスタンスが驚くほど明確になったのが最大の収穫でした。自分が考えていたことと、実際の自分の習性・生態とはズレが有ったことが可視化されたというか、疑問だったことがストンと腹の中に落ちた感じがあります。というか自分、思ったよりエゴの塊だったというか、ものづくりへの執着が強いんだなあ、と……。

勿論ああいう場なのでお世辞交じりだったと思いますが、自分のブログやGitHub、自分の考え方について褒めて貰えた場面があったのも非常に嬉しかったです。逆に「え、こんな評価されていいものなの?」みたいな戸惑いもありました。まあ、採点が届いたらまた別の気分が有りそうな気もしますが。

もらってありがたかった質問など

面談の中で頂いてありがたかった質問をざっくり列挙します。

あなたが幸せにしたいのはダレ?

自分は「仕事で人を幸せにする」というのが大きなテーマだと思って面談に臨みましたが、この質問によって「自分が本当にやりたいのはまずものづくりで、その上で誰か(=誰でもいい)が幸せになればよい」と感じていることが見えました。

つまり、幸せになるのは同僚でも、今困っていて不幸な人でも、自分の作ったものでもっと幸せになる人でもよいということです。

『人が幸せになること』というのは自分にとってとても大きなテーマではありますが、自分がやりたいことそのものではなかったのかな、という感じです。

需要が有り技術的に簡単なやり方と、需要が無く技術的に難しいやり方、どっちを選ぶ?

この質問は個人的に一番衝撃が有りました。冷静に考えれば『需要が有り技術的に簡単なやり方』を選ぶのが妥当だと思いますが、思い返すと自分は人生の中で『需要が無く技術的に難しいやり方』を積極的に選んでいたように思います。

 この問いに対する現状での自分の答えは、「そうなったら転職する」です。多分自分が納得いくものづくりができなければ不満を抱えて辛くなるだけですし(というか現状不満を抱えて辛くなっています。。。)、そうなってしまえばその職場に居ても迷惑を掛けるだけでしょう。

変な言い方ですが、この質問で自分の正体が明らかになったような気分があります。

理念は実態としてどうなっている?

個人的には理念に共感できる会社で働きたいと思っていましたが、「その理念はちゃんと運用されているか、どうやって確認するの?」という突っ込みを頂きました。

この件については複数の方から確認法など教えて頂きました。内容をざっくり列挙します。

  • 『理念に基づく評価・採用・教育制度が有るか』を人事の人に聞いて、即答できなければ大体ダメ。特に『どんな人を採りたいか』が理念とマッチしてない場合は危険。
  • ベンチャーは余程CEOが有能じゃないとちゃんとした理念を持ってない。
  • イベントでは人事さんが営業モードなので、エンジニアの方のSNSを確認したり、勉強会で会ったときに聞くのがよい。

面談とは直接関係ない話でしたが、個人的に重要だと思ったので残します。

今後の行動指針

「とりあえず自分はまずものづくりがしたいんだ」ということが明確になったので、インターンや就職ではそれを重視していこうと思います。『やりがいを感じられるものづくりをすること』をベースに、自分が幸せになれるだけの金、達成されてくれると嬉しい理念など、と順番をつけて追いかけるのがいいかなと(勿論全部欲しいですが)。

現状学校生活が「やりがいを感じられなくなりつつあり、金にもなっていない、余暇も無い。ついでに夏休みも研究室の都合で削られそう」という状態なので、割と真面目に大学院辞めて就職という選択肢ができつつあるなと感じています。

まとめ

全体を通してとてもいい体験ができました。研究室で布教したいと思います。

少し残念だったのは、折角全国から強そうな学生が集まっていたのに、あまり話をする時間が取れなかったことでしょうか。

何人かの方とつながりはできたので、今後仲良くさせてもらえると嬉しいなと思います。

その他

まともに話すのはン年ぶりの知り合いと会場でバッタリ会ったのは心臓が飛び出るかと思いました。

*1:記事執筆中にはまだ採点が届いてません